[newbie] A question about lists and strings

Discussion in 'Python' started by Mok-Kong Shen, Aug 10, 2012.

  1. In an earlier question about lists, I was told about the issue of
    creation of local names in a function. However, I still can't
    understand why the program below outputs:

    [999] sss
    [999]

    and not two identical lines of output. For both operators "+=" should
    anyway work in similar manner in the function xx in my view.

    Thanks for your help in advance.

    M. K. Shen

    ----------------------------------------------------------

    def xx(list,str):
    list+=[999]
    str+="sss"

    lista=[]
    stra=""
    lista+=[999]
    stra+="sss"
    print(lista,stra)

    listb=[]
    strb=""
    xx(listb,strb)
    print(listb,strb)
     
    Mok-Kong Shen, Aug 10, 2012
    #1
    1. Advertising

  2. 10.08.2012, × 13:19, Mok-Kong Shen ÎÁÐÉÓÁÌ(Á):

    >
    > In an earlier question about lists, I was told about the issue of
    > creation of local names in a function. However, I still can't
    > understand why the program below outputs:
    >
    > [999] sss
    > [999]
    >
    > and not two identical lines of output. For both operators "+=" should
    > anyway work in similar manner in the function xx in my view.
    >
    > Thanks for your help in advance.
    >
    > M. K. Shen
    >
    > ----------------------------------------------------------
    >
    > def xx(list,str):
    > list+=[999]
    > str+="sss"
    >
    > lista=[]
    > stra=""
    > lista+=[999]
    > stra+="sss"
    > print(lista,stra)
    >
    > listb=[]
    > strb=""
    > xx(listb,strb)
    > print(listb,strb)
    > --
    > http://mail.python.org/mailman/listinfo/python-list


    It seems like your xx() function doesn't return local str parameter and prints the global empty str, whereas it mutates the global list.

    Roman
     
    Roman Vashkevich, Aug 10, 2012
    #2
    1. Advertising

  3. 10.08.2012, × 13:28, Roman Vashkevich ÎÁÐÉÓÁÌ(Á):

    > 10.08.2012, × 13:19, Mok-Kong Shen ÎÁÐÉÓÁÌ(Á):
    >
    >>
    >> In an earlier question about lists, I was told about the issue of
    >> creation of local names in a function. However, I still can't
    >> understand why the program below outputs:
    >>
    >> [999] sss
    >> [999]
    >>
    >> and not two identical lines of output. For both operators "+=" should
    >> anyway work in similar manner in the function xx in my view.
    >>
    >> Thanks for your help in advance.
    >>
    >> M. K. Shen
    >>
    >> ----------------------------------------------------------
    >>
    >> def xx(list,str):
    >> list+=[999]
    >> str+="sss"
    >>
    >> lista=[]
    >> stra=""
    >> lista+=[999]
    >> stra+="sss"
    >> print(lista,stra)
    >>
    >> listb=[]
    >> strb=""
    >> xx(listb,strb)
    >> print(listb,strb)
    >> --
    >> http://mail.python.org/mailman/listinfo/python-list

    >
    > It seems like your xx() function doesn't return local str parameter and prints the global empty str, whereas it mutates the global list.
    >
    > Roman


    Excuse me for the mess I just did in my message:)
    The function doesn't print anything of cause. It takes list by reference and creates a new local str.
    When it's called with listb and strb arguments, listb is passed by reference and mutated. A string "sss" is concatenated with an empty local str. Nothing more happens. Since local str is not returned by xx(), it can not be expected to be printed out in the statement that follows. What is printed out in the print statement is the mutated listb and the global strb.

    RV
     
    Roman Vashkevich, Aug 10, 2012
    #3
  4. Mok-Kong Shen

    Peter Otten Guest

    Mok-Kong Shen wrote:

    >
    > In an earlier question about lists, I was told about the issue of
    > creation of local names in a function. However, I still can't
    > understand why the program below outputs:
    >
    > [999] sss
    > [999]
    >
    > and not two identical lines of output. For both operators "+=" should
    > anyway work in similar manner in the function xx in my view.
    >
    > Thanks for your help in advance.
    >
    > M. K. Shen
    >
    > ----------------------------------------------------------
    >
    > def xx(list,str):
    > list+=[999]
    > str+="sss"


    a += b

    is internally translated into to

    a = a.__iadd__(b)

    If the 'list' class were implemented in Python it would look like

    class list:
    def __iadd__(self, other):
    for item in other:
    self.append(item)
    return self

    i. e. the original list is modified when you perform += and you'll see the
    modification when you look at any name bound to that original list:

    b = a = [1, 2]
    a += [3, 4]
    print a # [1, 2, 3, 4]
    print b # [1, 2, 3, 4]

    Strings on the other hand are "immutable" -- they cannot be altered after
    the initial creation. The hypothetical __iadd__() implementation is

    class str:
    def __iadd__(self, other):
    return self + other

    So += rebinds a name to a new string:

    b = a = "first"
    a += "second"
    print b # first
    print a # firstsecond

    Armed with this knowledge

    > lista=[]
    > stra=""
    > lista+=[999]


    [999] is appended to lista and lista is rebound to itself.

    > stra+="sss"


    A new string "" + "sss" is created and stra is bound to that new string.

    > print(lista,stra)



    > listb=[]
    > strb=""
    > xx(listb,strb)


    Inside xx()

    (1) 999 is appended to listb and the local variable list is rebound.
    (2) A new string "" + "sss" is created and bound to the local variable str.

    > print(listb,strb)


    If you have understood the above here's a little brain teaser:

    >>> a = ([1,2,3],)
    >>> a[0] += [4, 5]

    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment

    >>> a[0]


    What are the contents of a[0] now?
     
    Peter Otten, Aug 10, 2012
    #4
  5. Mok-Kong Shen

    Dave Angel Guest

    On 08/10/2012 05:19 AM, Mok-Kong Shen wrote:
    >
    > In an earlier question about lists, I was told about the issue of
    > creation of local names in a function. However, I still can't
    > understand why the program below outputs:
    >
    > [999] sss
    > [999]
    >
    > and not two identical lines of output. For both operators "+=" should
    > anyway work in similar manner in the function xx in my view.
    >
    > Thanks for your help in advance.
    >
    > M. K. Shen
    >
    > ----------------------------------------------------------
    >
    > def xx(list,str):
    > list+=[999]
    > str+="sss"
    >
    > lista=[]
    > stra=""
    > lista+=[999]
    > stra+="sss"
    > print(lista,stra)
    >
    > listb=[]
    > strb=""
    > xx(listb,strb)
    > print(listb,strb)


    I'm rewriting your xx function so it doesn't overwrite reserved words,
    list and str.

    def xx(mylist, mystring):
    mylist += [999]
    mystring += "xxx"

    There are a couple of effects you need to understand in order to see
    what's happening.

    First, some terminology. You don't assign values in Python, you bind a
    name or another lvalue to an object. a=650 builds an object of type
    int, and binds it to the name a. if you then say b=a, you bind b to the
    *SAME* object, the previously created int object of value 650. It can
    be useful to print the id() of an object, to convince yourself that
    they're actually the same. So if you print id(a) and id(b), you'll get
    the same value. But if you said c=651 and d=651, you'd have two
    objects, and the two names would be bound to different objects, with
    different ids.

    The += operator is an example of an augmented assignment operator.
    Others that behave the same way include *= -= and so on.

    The += operator tries to modify the object referred to by the left hand
    side, by modifying that object in place. If that fails, perhaps because
    the object is immutable, then it builds a new object, and binds the left
    side to that new object.

    So, if you have a list, and you use += to append to it, you still have
    the same list object, but it's longer now. If you have a string, or an
    int, or ... that's immutable, then it builds a new one, and binds it to
    the left side.

    Now, put those two operations inside a function, and what do we have ?
    Let's go through the function, looking at what happens.

    Local parameter mylist gets bound to the list object bound to listb.
    The object is not copied, it's now bound to new names, one in the global
    space, one local. When the mylist += statement is executed, it
    modifies that object, and instantly the global "variable" listb is modified.

    Local parameter mystring gets bound to the string object bound to strb.
    We still only have one (immutable) object. When the mystring +=
    statement is executed, it creates a new string object by concatenating
    the old one and the "xxx" string. At this point, we can print
    id(mystring) and see that it changed. When the function returns, the
    local symbols are unbound, and the new string object is discarded since
    there are no longer any refs.

    At this point, in top-level code, the listb object has been modified,
    and the strb one has not; it still is bound to the old value.



    --

    DaveA
     
    Dave Angel, Aug 10, 2012
    #5
  6. Am 10.08.2012 12:07, schrieb Dave Angel:
    [snip]
    > At this point, in top-level code, the listb object has been modified,
    > and the strb one has not; it still is bound to the old value.


    This means there is no way of modifying a string at the top level
    via a function, excepting through returning a new value and assigning
    that to the string name at the top level. Please again correct me, if
    I am wrong.

    M. K. Shen
     
    Mok-Kong Shen, Aug 10, 2012
    #6
  7. On Fri, Aug 10, 2012 at 8:12 PM, Mok-Kong Shen
    <> wrote:
    > Thanks for the explanation of the output obtained. But this means
    > nonetheless that parameters of types lists and strings are dealt with
    > in "inherently" (semantically) different ways by Python, right?


    It's nothing to do with parameters, but yes, lists are mutable and
    strings are immutable. A tuple will behave the same way a string does:

    >>> a

    (1, 2, 3, 4)
    >>> b=a
    >>> a+=5, # note that "5," is a one-element tuple
    >>> a

    (1, 2, 3, 4, 5)
    >>> b

    (1, 2, 3, 4)


    By the way:

    On Fri, Aug 10, 2012 at 8:07 PM, Dave Angel <> wrote:
    > But if you said c=651 and d=651, you'd have two
    > objects, and the two names would be bound to different objects, with
    > different ids.


    To be more accurate, you *may* have two different objects. It's
    possible for things to be optimized (eg with small numbers, or with
    constants compiled at the same time).

    ChrisA
     
    Chris Angelico, Aug 10, 2012
    #7
  8. Mok-Kong Shen

    Dave Angel Guest

    On 08/10/2012 06:12 AM, Mok-Kong Shen wrote:
    > Am 10.08.2012 11:48, schrieb Roman Vashkevich:
    >> [snip]
    > >The function .... It takes list by reference and creates a new local
    > > str. When it's called with listb and strb arguments, listb is passed
    > > by reference and mutated. A string "sss" is concatenated with an
    > > empty local str. Nothing more happens. Since local str is not
    > > returned by xx(), it can not be expected to be printed out in the
    > > statement that follows. What is printed out in the print statement is
    > > the mutated listb and the global strb.

    >
    > Thanks for the explanation of the output obtained. But this means
    > nonetheless that parameters of types lists and strings are dealt with
    > in "inherently" (semantically) different ways by Python, right?
    >
    > M. K. Shen
    >


    Nothing to do with parameters. Lists are mutable, and strings are
    immutable, so += behaves differently.



    --

    DaveA
     
    Dave Angel, Aug 10, 2012
    #8
  9. On Fri, Aug 10, 2012 at 8:31 PM, Mok-Kong Shen
    <> wrote:
    > This means there is no way of modifying a string at the top level
    > via a function, excepting through returning a new value and assigning
    > that to the string name at the top level. Please again correct me, if
    > I am wrong.


    Yes, but you can (ab)use a one-element list as a pointer.

    >>> foo=["Hello"]
    >>> def mutate(ptr):

    ptr[0]="World"

    >>> mutate(foo)
    >>> print(foo[0])

    World

    But it's probably worth thinking about exactly why you're wanting to
    change that string, and what you're really looking to accomplish.
    There may well be a better way.

    Chris Angelico
     
    Chris Angelico, Aug 10, 2012
    #9
  10. Am 10.08.2012 12:40, schrieb Chris Angelico:

    > But it's probably worth thinking about exactly why you're wanting to
    > change that string, and what you're really looking to accomplish.
    > There may well be a better way.


    My problem is the following: I have at top level 3 lists and 3 strings:
    lista, listb, listc and stra, strb, strc. I can with a single function
    call change all 3 list values. How could I realize similar changes
    of the 3 string values with a single function call?

    Thanks in advance.

    M. K. Shen
     
    Mok-Kong Shen, Aug 10, 2012
    #10
  11. Mok-Kong Shen

    Dave Angel Guest

    On 08/10/2012 06:31 AM, Mok-Kong Shen wrote:
    > Am 10.08.2012 12:07, schrieb Dave Angel:
    > [snip]
    >> At this point, in top-level code, the listb object has been modified,
    >> and the strb one has not; it still is bound to the old value.

    >
    > This means there is no way of modifying a string at the top level
    > via a function, excepting through returning a new value and assigning
    > that to the string name at the top level. Please again correct me, if
    > I am wrong.
    >
    > M. K. Shen
    >


    You're close. There are three ways I can think of. The "right" way is
    to return a value, which the caller can use any way he wants, including
    binding it to a global.

    Second is to declare the name as global, rather than taking the object
    as a formal parameter. In this case, you're taking on the
    responsibility for managing that particular global, by its correct name.
    def yy():
    global strb
    strb += "whatever"

    Third is to hold the string in some more complex structure which is
    mutable. (untested, may contain typos)
    def zz(mydict):
    mydict["key1"] += "text"

    called as:
    globaldict = {"key1": "initial ", "key2": "init"}




    --

    DaveA
     
    Dave Angel, Aug 10, 2012
    #11
  12. Mok-Kong Shen

    Dave Angel Guest

    On 08/10/2012 06:37 AM, Chris Angelico wrote:
    > On Fri, Aug 10, 2012 at 8:12 PM, Mok-Kong Shen
    > <> wrote:
    >> Thanks for the explanation of the output obtained. But this means
    >> nonetheless that parameters of types lists and strings are dealt with
    >> in "inherently" (semantically) different ways by Python, right?

    > It's nothing to do with parameters, but yes, lists are mutable and
    > strings are immutable. A tuple will behave the same way a string does:
    >
    >>>> a

    > (1, 2, 3, 4)
    >>>> b=a
    >>>> a+=5, # note that "5," is a one-element tuple
    >>>> a

    > (1, 2, 3, 4, 5)
    >>>> b

    > (1, 2, 3, 4)
    >
    >
    > By the way:
    >
    > On Fri, Aug 10, 2012 at 8:07 PM, Dave Angel <> wrote:
    >> But if you said c=651 and d=651, you'd have two
    >> objects, and the two names would be bound to different objects, with
    >> different ids.

    > To be more accurate, you *may* have two different objects. It's
    > possible for things to be optimized (eg with small numbers, or with
    > constants compiled at the same time).
    >
    > ChrisA


    You're right, of course. But I picked the value of 650+ deliberately,
    as I believe CPython doesn't currently optimize ints over 256.



    --

    DaveA
     
    Dave Angel, Aug 10, 2012
    #12
  13. 10.08.2012, × 14:12, Mok-Kong Shen ÎÁÐÉÓÁÌ(Á):

    > Am 10.08.2012 11:48, schrieb Roman Vashkevich:
    >> [snip]
    > >The function .... It takes list by reference and creates a new local
    > > str. When it's called with listb and strb arguments, listb is passed
    > > by reference and mutated. A string "sss" is concatenated with an
    > > empty local str. Nothing more happens. Since local str is not
    > > returned by xx(), it can not be expected to be printed out in the
    > > statement that follows. What is printed out in the print statement is
    > > the mutated listb and the global strb.

    >
    > Thanks for the explanation of the output obtained. But this means
    > nonetheless that parameters of types lists and strings are dealt with
    > in "inherently" (semantically) different ways by Python, right?
    >
    > M. K. Shen
    >
    > --
    > http://mail.python.org/mailman/listinfo/python-list


    I am not sure I understand your question. Can you rephrase it or make it more explicit?

    RV
     
    Roman Vashkevich, Aug 10, 2012
    #13
  14. Mok-Kong Shen

    Dave Angel Guest

    On 08/10/2012 06:48 AM, Mok-Kong Shen wrote:
    > Am 10.08.2012 12:40, schrieb Chris Angelico:
    >
    >> But it's probably worth thinking about exactly why you're wanting to
    >> change that string, and what you're really looking to accomplish.
    >> There may well be a better way.

    >
    > My problem is the following: I have at top level 3 lists and 3 strings:
    > lista, listb, listc and stra, strb, strc. I can with a single function
    > call change all 3 list values. How could I realize similar changes
    > of the 3 string values with a single function call?
    >
    > Thanks in advance.
    >
    > M. K. Shen
    >
    >

    Make sure your function returns the values, rather than just trying to
    mutate them in place.

    def aa(mystring1, mystring2, mystring3):
    mystring1 += "something"
    mystring2 += "otherthing"
    mystring3 += "nobody"
    return mystring1, mystring2, mystring3

    str1, str2, str3 = aa(str1, str2, str3)



    --

    DaveA
     
    Dave Angel, Aug 10, 2012
    #14
  15. On Fri, Aug 10, 2012 at 8:56 PM, Dave Angel <> wrote:
    >> On Fri, Aug 10, 2012 at 8:07 PM, Dave Angel <> wrote:
    >>> But if you said c=651 and d=651, you'd have two
    >>> objects, and the two names would be bound to different objects, with
    >>> different ids.

    >> To be more accurate, you *may* have two different objects. It's
    >> possible for things to be optimized (eg with small numbers, or with
    >> constants compiled at the same time).

    >
    > You're right, of course. But I picked the value of 650+ deliberately,
    > as I believe CPython doesn't currently optimize ints over 256.


    Yep. Also:

    >>> a=651; b=651
    >>> a is b

    True
    >>> a=651
    >>> a is b

    False

    Same thing occurs (or at least, appears to) with constants in the same
    module. Could potentially be a fairly hefty optimization, if you use
    the same numbers all the time (bit flags, scale factors, modulo
    divisors, etc, etc, etc).

    Still doesn't alter your fundamental point of course.

    ChrisA
     
    Chris Angelico, Aug 10, 2012
    #15
  16. 10.08.2012, × 14:12, Mok-Kong Shen ÎÁÐÉÓÁÌ(Á):

    > Am 10.08.2012 11:48, schrieb Roman Vashkevich:
    >> [snip]
    > >The function .... It takes list by reference and creates a new local
    > > str. When it's called with listb and strb arguments, listb is passed
    > > by reference and mutated. A string "sss" is concatenated with an
    > > empty local str. Nothing more happens. Since local str is not
    > > returned by xx(), it can not be expected to be printed out in the
    > > statement that follows. What is printed out in the print statement is
    > > the mutated listb and the global strb.

    >
    > Thanks for the explanation of the output obtained. But this means
    > nonetheless that parameters of types lists and strings are dealt with
    > in "inherently" (semantically) different ways by Python, right?
    >
    > M. K. Shen
    >
    > --
    > http://mail.python.org/mailman/listinfo/python-list


    DaveA provided a very explicit explanation of how py handles list and string objects.
    Parameters are handled "inherently" by functions...

    RV
     
    Roman Vashkevich, Aug 10, 2012
    #16
  17. Am 10.08.2012 12:56, schrieb Roman Vashkevich:

    > I am not sure I understand your question. Can you rephrase it or make it more explicit?


    I have just detailed my problem and Dave Angel has shown how to solve
    it properly.

    M. K. Shen
     
    Mok-Kong Shen, Aug 10, 2012
    #17
  18. Mok-Kong Shen

    Rotwang Guest

    On 10/08/2012 10:59, Peter Otten wrote:
    > [...]
    >
    > If you have understood the above here's a little brain teaser:
    >
    >>>> a = ([1,2,3],)
    >>>> a[0] += [4, 5]

    > Traceback (most recent call last):
    > File "<stdin>", line 1, in <module>
    > TypeError: 'tuple' object does not support item assignment
    >
    >>>> a[0]

    >
    > What are the contents of a[0] now?


    Ha, nice.


    --
    I have made a thing that superficially resembles music:

    http://soundcloud.com/eroneity/we-berated-our-own-crapiness
     
    Rotwang, Aug 10, 2012
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. robin
    Replies:
    10
    Views:
    551
    Dave Hansen
    Apr 12, 2006
  2. =?UTF-8?B?w4FuZ2VsIEd1dGnDqXJyZXogUm9kcsOtZ3Vleg==

    List of lists of lists of lists...

    =?UTF-8?B?w4FuZ2VsIEd1dGnDqXJyZXogUm9kcsOtZ3Vleg==, May 8, 2006, in forum: Python
    Replies:
    5
    Views:
    424
    =?UTF-8?B?w4FuZ2VsIEd1dGnDqXJyZXogUm9kcsOtZ3Vleg==
    May 15, 2006
  3. ardief
    Replies:
    14
    Views:
    749
    Paddy
    Feb 3, 2007
  4. Ben

    Strings, Strings and Damned Strings

    Ben, Jun 22, 2006, in forum: C Programming
    Replies:
    14
    Views:
    787
    Malcolm
    Jun 24, 2006
  5. vimal
    Replies:
    3
    Views:
    107
    Jim Cochrane
    Jun 28, 2008
Loading...

Share This Page