Python and object / class attrs - what happens?

Can someone explain why Python is doing the following?

>>> class Foo(object): ... bar = [] ... >>> a = Foo() >>> b = Foo() >>> a.bar.append(1) >>> b.bar [1] >>> a.bar = 1 >>> a.bar 1 >>> b.bar [1] >>> a.bar = [] >>> a.bar [] >>> b.bar [1] >>> del a.bar >>> a.bar [1] 

This is pretty confusing!

+1
source share
6 answers

As others have argued, code written as written creates a class variable, not an instance variable. You need to assign in __init__ to create an instance variable.

Hopefully this annotated copy of your code will help explain what happens at each step:

 >>> class Foo(object): ... bar = [] # defines a class variable on Foo (shared by all instances) ... >>> a = Foo() >>> b = Foo() >>> a.bar.append(1) # appends the value 1 to the previously empty list Foo.bar >>> b.bar # returns the value of the class variable Foo.bar [1] >>> a.bar = 1 # binds 1 to the instance variable a.bar, masking the access >>> a.bar # you previously had to the class variable through a.bar 1 >>> b.bar # b doesn't have an instance variable 'bar' so this still [1] # returns the class variable >>> a.bar = [] # bind a instance variable to to an empty list >>> a.bar [] >>> b.bar # b doesn't have an instance variable 'bar' so this still [1] # returns the class variable >>> del a.bar # unbinds a instance variable unmasking the class variable >>> a.bar # so a.bar now returns the list with 1 in it. [1] 

Also, print out the value of Foo.bar (a class variable accessible through the class, not through the instance) after each of your statements can help clarify what is happening.

0
source

This is because, as you wrote it, bar is a class variable, not an instance variable.

To define an instance variable, bind it in the constructor:

 class Foo(object): def __init__(self): self.bar = [] 

Note that now it belongs to one instance of Foo ( self ), and not to the Foo class, and you will see the results expected when it was assigned.

+7
source

When you declare an element in this class, it is shared by all instances of the class. To create the correct member of a class that belongs to each instance separately, create it in __init__ as follows:

 class Foo(object): def __init__(self): self.bar = [] 
0
source

At the beginning, bar is a class variable, and it is split between a and b , both a.bar and b.bar refer to the same object.

When you assign a new value to a.bar , it does not overwrite the class variable, it adds a new instance variable to object a , hiding the class variable when accessing a.bar . If you delete a.bar (the instance variable), then a.bar again goes to the class variable again.

b.bar , on the other hand, always refers to a class variable; it is not affected by the extra bar on object a or any values ​​assigned to it.

To set a class variable, you can access it through the class itself:

 Foo.bar = 1 
0
source
 >>> class Foo(object): ... bar = [] ... 

bar is a general class variable, not an instance variable. I believe this applies to most of your confusion. To make it an instance of var, define it in the __init__ class for other answers.

 >>> a = Foo() >>> b = Foo() >>> a.bar.append(1) >>> b.bar [1] 

This proves it.

 >>> a.bar = 1 >>> a.bar 1 >>> b.bar [1] 

You have now redefined a.bar as an instance variable. This happens when you define external variables by default.

 >>> a.bar = [] >>> a.bar [] >>> b.bar [1] >>> del a.bar >>> a.bar [1] 

Same. b.bar is still a common class variable.

0
source

In the corresponding note, you should know about this trap, which you can see in the near future:

 class A: def __init__(self, mylist = []): self.mylist = mylist a = A() a2 = A() a.mylist.append(3) print b.mylist #prints [3] ??? 

This confuses many people and is related to how the code is interpreted. Python first interprets the function headers, so it evaluates __init__(self, mylist = []) and saves the link to this list as the default parameter. This means that all instances of A will (unless their own list) be referenced in the original list. The right code to do such a thing would be

 class A: def __init__(self, mylist=None): if mylist: self.mylist = mylist else: self.mylist = [] 

or if you want a shorter expression, you can use the triple syntax:

 self.mylist = mylist if mylist else [] 
0
source

Source: https://habr.com/ru/post/904967/


All Articles