Why does += behave unexpectedly on lists?

后端 未结 8 1240
春和景丽
春和景丽 2020-11-21 07:20

The += operator in python seems to be operating unexpectedly on lists. Can anyone tell me what is going on here?

class foo:  
     bar = []
            


        
8条回答
  •  感动是毒
    2020-11-21 08:11

    Although much time has passed and many correct things were said, there is no answer which bundles both effects.

    You have 2 effects:

    1. a "special", maybe unnoticed behaviour of lists with += (as stated by Scott Griffiths)
    2. the fact that class attributes as well as instance attributes are involved (as stated by Can Berk Büder)

    In class foo, the __init__ method modifies the class attribute. It is because self.bar += [x] translates to self.bar = self.bar.__iadd__([x]). __iadd__() is for inplace modification, so it modifies the list and returns a reference to it.

    Note that the instance dict is modified although this would normally not be necessary as the class dict already contains the same assignment. So this detail goes almost unnoticed - except if you do a foo.bar = [] afterwards. Here the instances's bar stays the same thanks to the said fact.

    In class foo2, however, the class's bar is used, but not touched. Instead, a [x] is added to it, forming a new object, as self.bar.__add__([x]) is called here, which doesn't modify the object. The result is put into the instance dict then, giving the instance the new list as a dict, while the class's attribute stays modified.

    The distinction between ... = ... + ... and ... += ... affects as well the assignments afterwards:

    f = foo(1) # adds 1 to the class's bar and assigns f.bar to this as well.
    g = foo(2) # adds 2 to the class's bar and assigns g.bar to this as well.
    # Here, foo.bar, f.bar and g.bar refer to the same object.
    print f.bar # [1, 2]
    print g.bar # [1, 2]
    
    f.bar += [3] # adds 3 to this object
    print f.bar # As these still refer to the same object,
    print g.bar # the output is the same.
    
    f.bar = f.bar + [4] # Construct a new list with the values of the old ones, 4 appended.
    print f.bar # Print the new one
    print g.bar # Print the old one.
    
    f = foo2(1) # Here a new list is created on every call.
    g = foo2(2)
    print f.bar # So these all obly have one element.
    print g.bar 
    

    You can verify the identity of the objects with print id(foo), id(f), id(g) (don't forget the additional ()s if you are on Python3).

    BTW: The += operator is called "augmented assignment" and generally is intended to do inplace modifications as far as possible.

提交回复
热议问题