Python mutable class variable vs immutable class variable - python

Running the sample code below:
class S:
i = 0
a = []
def __init__(self):
self.i += 1
self.a.append(1)
s1 = S()
print((s1.i, s1.a))
s2 = S()
print((s2.i, s2.a))
The output will be:
(1, [1])
(1, [1, 1])
My question is why the int S.i reset to 0 for s2 but the list S.a does not reset to empty? I think it has something to do with the immutable int vs mutable list but could someone help to express more details what happened to the two class variables during the two init calls? Thanks!

So you are altering the instance attributes when you call s1.i or s1.a. To change the class attributes try this:
S.i += 1
S.a.append(1)
In your constructor you initialise self.a and self.i. this creates instance attributes that belong to each instance of the class.
The a and the i declared outside the constructor are class attributes and are shared by all instances.
The reason s1.a and S.a updates regardless of which attribute is used is because lists are mutable and both the instance and class variables are references to the same list.

self.i += 1
is equivalent to
self.i = self.i + 1
When the instance variable does not exist, the value is looked up on the class, so in this scenario, it is equivalent to
self.i = S.i + 1
After you define self.i, then any further value lookup is on the instance variable, not on the class variable. So after this line, you have S.i = 0 and s1.i = 1. Since S.i is not modified, s2.i also becomes 1.
On the other hand,
self.a.append(1)
does not create a new instance variable, but appends an element to the existing class variable.

The way this particular code is written abstracts some of what Python is doing behind the scenes here, so let's go through it.
When you define the class, and you define variables outside of any function like you do at the beginning in your code, it creates class attributes. These are shared among all instances of your class (in your case, s1 and s2 are both sharing the same reference to your i object and your a object).
When you initialize the class, you are calling the __init__ function, which, in your code, first calls self.i += 1, and I think this is where most of the confusion is coming from. In Python, integers are immutable, so they cannot be overridden. By calling +=, you are removing the reference to your old i variable and creating a new one referencing a different place in memory. But because you are now in a function in your class, it's being defined as an instance attribute. Instance attributes are not shared among different instances of your class.
However, lists are mutable. So when you append 1 to your list, you are not creating a new instance variable, so it keeps the same reference to the class attribute, and therefore when you initialize your class the second time, it adds it onto the class attribute that already has been populated once when you created the first instance.

class S:
i = 0
a = []
def __init__(self):
self.i += 1
self.a.append(1)
the list as defined by a = [] is a class attribute. It's instantiated when the class is defined, and remains the same list object. Any instances of this class are going to reference the one list.
If you want to have an empty list for every new instance, then move the list definition to within the __init__ method:
class S:
i = 0
def __init__(self):
self.a = []
self.i += 1
self.a.append(1)
Result:
>>> s1 = S()
>>> print((s1.i, s1.a))
(1, [1])
>>>
>>> s2 = S()
>>> print((s2.i, s2.a))
(1, [1])

Related

Python Class Attributes default

class A():
count = 0
print(count)
def __init__(self):
A.count+=1
def exclaim(self):
print("I'm an A")
#classmethod
def kids(cls):
print("A has", cls.count,"little objects.")
My question is that I create a object with "t1=A()". And A.count should be 1. I understand. And then, if I create second object with "t2=A()". I don't understand why A.count = 2. I thought when used A(), it will make count back to default 0.
In A, count is a class variable, meaning there is one copy of the variable for the class. Instances of the class continue to pick up that value.
In __init__, you have A.count += 1. This modifies the class variable directly, without creating an instance variable. So there's still only one copy of count, and all instances pick up the current value.
If you instead change it to self.count += 1, then you'll get the behavior you expect. Initially, each instance will pick up the value from the class. But when self.count += 1 is executed, it will add 1 to count and then store the result in an instance variable that is private to the instance. Thereafter, the instance will have its own copy of count which is independent from the class variable, or the instance variables in other instances of the class.

python global variable from within class not updating by class method

I am making a chess playing AI and have a minimax algorithm that currently returns the state of the game after choosing a move: board, white_pieces and black_pieces.
My chess class handles move generation and maintenance of the state. It has some global variables:
__board
__white_pieces
__black_pieces
Which are declared within the class: body but outside of any methods. This should make it a global variable.
At the beginning of the game, these are initialized using a build_board method (not always implemented in order to test the code). I suppose it could be moved to an init() method at this point now that I have tested the code.
def build_board(self):
for i in range(1, 17):
self.__board[self.__white_pieces[i][1]] = i
self.__white_pieces[i][3] = True
self.__board[self.__black_pieces[i][1]] = -1 * i
self.__black_pieces[i][3] = True
for i in range(0, 20):
self.__board[i] = 99
for i in range(100, 120):
self.__board[i] = 99
for i in range(0, 8):
self.__board[20 + 10 * i] = 99
self.__board[20 + 10 * i + 9] = 99
I'm assuming this makes sense because it uses a list which is a mutable type. Sure, no problem. This edits the board.
Then, when I try to make the game take the state of the game returned by minimax and update those global variables, neither of these seem to update it:
def play_move(self,board,white_pieces,black_pieces):
global __board
global __white_pieces
global __black_pieces
__board = board
__white_pieces = white_pieces
__black_pieces = black_pieces
or
def play_move(self,board,white_pieces,black_pieces):
self.__board = board
self.__white_pieces = white_pieces
self.__black_pieces = black_pieces
Am I misunderstanding something about how global variables work within a class? If someone could clear this up for me that would be great. Thanks!
Edit:
I can even make the playmove methods return the self.__board,self.__white_pieces,self.__black_pieces tuple and print out if they are actually the updated move and they are. Within the method, and passing those results out to another method, states that self.__xxxxx has been updated. From outside of that method though, it does not appear to update the global variables.
Variables declared " within the class: body but outside of any methods" become class attributes, not global variables. Class attributes are shared between all instances of the class, so that's probably not what you want here.
Note that unless there's an instance attribute by the same name shadowing it, you can access a class attribute (and mutate it) from an instance, but as soon as you set (not mutate) this attribute on an instance, it will create an instance attribute that will shadow the class attribute:
>>> class Foo(object):
... bar = []
... def add(self, x):
... self.bar.append(x)
... def set(self, bar):
... self.bar = bar
...
>>> f1 = Foo()
>>> f1.bar
[]
>>> id(f1.bar)
140281150134752
>>> f2 = Foo()
>>> f2.bar
[]
>>> id(f2.bar)
140281150134752
>>> f2.bar is f1.bar
True
>>> f1.add(42)
>>> f2.bar
[42]
>>> f1.bar
[42]
>>> f1.set([33])
>>> f1.bar
[33]
>>> f2.bar
[42]
>>> f2.bar is Foo.bar
True
>>> f1.bar is Foo.bar
False
>>>
Given your use case and snippet, I think that you really want your board etc to be instance attributes instead of globals or class attributes.
You mention that:
Each method needs to be able to access the current state of the board and modify it.
but that's exactly the point of having instance attributes: all methods of an object share the object's state (=> attributes).
As a side note, don't prefix your attributes with a double underscore unless you really know what it does and really need it (hint: you might need this perhaps once every 10 years or so...). A single leading underscore is the convention to denote it's an implementation detail and not part of your class API.

Python doesn't allocate new space for objects instantiated outside the constructor of a class-- expected behavior? [duplicate]

Is there any meaningful distinction between:
class A(object):
foo = 5 # some default value
vs.
class B(object):
def __init__(self, foo=5):
self.foo = foo
If you're creating a lot of instances, is there any difference in performance or space requirements for the two styles? When you read the code, do you consider the meaning of the two styles to be significantly different?
There is a significant semantic difference (beyond performance considerations):
when the attribute is defined on the instance (which is what we usually do), there can be multiple objects referred to. Each gets a totally separate version of that attribute.
when the attribute is defined on the class, there is only one underlying object referred to, so if operations on different instances of that class both attempt to set/(append/extend/insert/etc.) the attribute, then:
if the attribute is a builtin type (like int, float, boolean, string), operations on one object will overwrite (clobber) the value
if the attribute is a mutable type (like a list or a dict), we will get unwanted leakage.
For example:
>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
... def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[]
The difference is that the attribute on the class is shared by all instances. The attribute on an instance is unique to that instance.
If coming from C++, attributes on the class are more like static member variables.
Here is a very good post, and summary it as below.
class Bar(object):
## No need for dot syntax
class_var = 1
def __init__(self, i_var):
self.i_var = i_var
## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)
## Finds i_var in foo's instance namespace
foo.i_var
## 2
## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1
And in visual form
Class attribute assignment
If a class attribute is set by accessing the class, it will override the value for all instances
foo = Bar(2)
foo.class_var
## 1
Bar.class_var = 2
foo.class_var
## 2
If a class variable is set by accessing an instance, it will override the value only for that instance. This essentially overrides the class variable and turns it into an instance variable available, intuitively, only for that instance.
foo = Bar(2)
foo.class_var
## 1
foo.class_var = 2
foo.class_var
## 2
Bar.class_var
## 1
When would you use class attribute?
Storing constants. As class attributes can be accessed as attributes of the class itself, it’s often nice to use them for storing Class-wide, Class-specific constants
class Circle(object):
pi = 3.14159
def __init__(self, radius):
self.radius = radius
def area(self):
return Circle.pi * self.radius * self.radius
Circle.pi
## 3.14159
c = Circle(10)
c.pi
## 3.14159
c.area()
## 314.159
Defining default values. As a trivial example, we might create a bounded list (i.e., a list that can only hold a certain number of elements or fewer) and choose to have a default cap of 10 items
class MyClass(object):
limit = 10
def __init__(self):
self.data = []
def item(self, i):
return self.data[i]
def add(self, e):
if len(self.data) >= self.limit:
raise Exception("Too many elements")
self.data.append(e)
MyClass.limit
## 10
Since people in the comments here and in two other questions marked as dups all appear to be confused about this in the same way, I think it's worth adding an additional answer on top of Alex Coventry's.
The fact that Alex is assigning a value of a mutable type, like a list, has nothing to do with whether things are shared or not. We can see this with the id function or the is operator:
>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
... def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False
(If you're wondering why I used object() instead of, say, 5, that's to avoid running into two whole other issues which I don't want to get into here; for two different reasons, entirely separately-created 5s can end up being the same instance of the number 5. But entirely separately-created object()s cannot.)
So, why is it that a.foo.append(5) in Alex's example affects b.foo, but a.foo = 5 in my example doesn't? Well, try a.foo = 5 in Alex's example, and notice that it doesn't affect b.foo there either.
a.foo = 5 is just making a.foo into a name for 5. That doesn't affect b.foo, or any other name for the old value that a.foo used to refer to.* It's a little tricky that we're creating an instance attribute that hides a class attribute,** but once you get that, nothing complicated is happening here.
Hopefully it's now obvious why Alex used a list: the fact that you can mutate a list means it's easier to show that two variables name the same list, and also means it's more important in real-life code to know whether you have two lists or two names for the same list.
* The confusion for people coming from a language like C++ is that in Python, values aren't stored in variables. Values live off in value-land, on their own, variables are just names for values, and assignment just creates a new name for a value. If it helps, think of each Python variable as a shared_ptr<T> instead of a T.
** Some people take advantage of this by using a class attribute as a "default value" for an instance attribute that instances may or may not set. This can be useful in some cases, but it can also be confusing, so be careful with it.
There is one more situation.
Class and instance attributes is Descriptor.
# -*- encoding: utf-8 -*-
class RevealAccess(object):
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
return self.val
class Base(object):
attr_1 = RevealAccess(10, 'var "x"')
def __init__(self):
self.attr_2 = RevealAccess(10, 'var "x"')
def main():
b = Base()
print("Access to class attribute, return: ", Base.attr_1)
print("Access to instance attribute, return: ", b.attr_2)
if __name__ == '__main__':
main()
Above will output:
('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)
The same type of instance access through class or instance return different result!
And i found in c.PyObject_GenericGetAttr definition,and a great post.
Explain
If the attribute is found in the dictionary of the classes which make up.
the objects MRO, then check to see if the attribute being looked up points to a Data Descriptor (which is nothing more that a class implementing both the __get__ and the __set__ methods).
If it does, resolve the attribute lookup by calling the __get__ method of the Data Descriptor (lines 28–33).

Python: derived classes access dictionary of base class in the same memory location

I'm wondering why a dictionary, that is defined in a base class and is accessed from derived classes, is obviously present only in one memory location.
A short example:
class BaseClass:
_testdict = dict()
_testint = 0
def add_dict_entry(self):
self._testdict["first"] = 1
def increment(self):
self._testint += 1
class Class1(BaseClass):
pass
class Class2(BaseClass):
pass
object1 = Class1()
object2 = Class2()
object1.add_dict_entry()
object1.increment()
print(object2._testdict)
print(object2._testint)
and the output is:
{'first': 1}
0
Why does a call to the "add_dict_entry" of object1 affect the dictionary of object2? Using integers ("increment") the base class variable is not affected.
Thanks a lot.
Lorenz
It's because _testdict is a class variable: it's defined only once, when the class is initially constructed. If you want it to be separate for each instance, make it an instance variable:
class BaseClass:
_testint = 0
def __init__(self):
self._testdict = dict()
def add_dict_entry(self):
self._testdict["first"] = 1
(Note that you'd need to create __init__ methods for Class1 and Class2 as well, both of which would have to call BaseClass.__init__(self)).
_testint behaves differently because you're performing a rebinding operation on it rather than a mutating operation. ints are immutable, so you can't "change" one- self._testint += 1 is just syntactic sugar for self._testint = self._testint + 1. Similarly, you can perform a rebinding operation on self._testdict that won't be shared between instances- for example, self._testdict = {} will reset only that instance's _testdict.
In python, int is immutable, therefore the += operation will rebound the class variable into an instance variables. On the other hand, a dictionary indexing mutates the dictionary in place. A more comparable example would be
def add_dict_entry(self):
# create a new dict
tmp = dict(self._testdict)
tmp["first"] = 1
# shadow the class variable with an instance variables
self._testdict = tmp

python class instance variables and class variables

I'm having a problem understanding how class / instance variables work in Python. I don't understand why when I try this code the list variable seems to be a class variable
class testClass():
list = []
def __init__(self):
self.list.append('thing')
p = testClass()
print p.list
f = testClass()
print f.list
Output:
['thing']
['thing', 'thing']
and when I do this it seems to be an instance variable
class testClass():
def __init__(self):
self.list = []
self.list.append('thing')
p = testClass()
print p.list
f = testClass()
print f.list
Output:
['thing']
['thing']
This is because of the way Python resolves names with the .. When you write self.list the Python runtime tries to resolve the list name first by looking for it in the instance object, and if it is not found there, then in the class instance.
Let's look into it step by step
self.list.append(1)
Is there a list name into the object self?
Yes: Use it! Finish.
No: Go to 2.
Is there a list name into the class instance of object self?
Yes: Use it! Finish
No: Error!
But when you bind a name things are different:
self.list = []
Is there a list name into the object self?
Yes: Overwrite it!
No: Bind it!
So, that is always an instance variable.
Your first example creates a list into the class instance, as this is the active scope at the time (no self anywhere). But your second example creates a list explicitly in the scope of self.
More interesting would be the example:
class testClass():
list = ['foo']
def __init__(self):
self.list = []
self.list.append('thing')
x = testClass()
print x.list
print testClass.list
del x.list
print x.list
That will print:
['thing']
['foo']
['foo']
The moment you delete the instance name the class name is visible through the self reference.
Python has interesting rules about looking up names. If you really want to bend your mind, try this code:
class testClass():
l = []
def __init__(self):
self.l = ['fred']
This will give each instance a variable called l that masks the class variable l. You will still be able to get at the class variable if you do self.__class__.l.
The way I think of it is this... Whenever you do instance.variable (even for method names, they're just variables who's values happen to be functions) it looks it up in the instance's dictionary. And if it can't find it there, it tries to look it up in the instance's class' dictionary. This is only if the variable is being 'read'. If it's being assigned to, it always creates a new entry in the instance dictionary.
In your first example, list is an attribute of the class, shared by all instances of it. This means that you can even access it without having an object of type testClass:
>>> class testClass():
... list = []
... def __init__(self):
... self.list.append("thing")
...
>>> testClass.list
[]
>>> testClass.list.append(1)
>>> testClass.list
[1]
But all objects share the list attribute with the class and each other:
>>> testObject = testClass()
>>> testObject.list
[1, 'thing']
>>> testClass.list
[1, 'thing']
>>>
>>> testObject2 = testClass()
>>> testClass.list
[1, 'thing', 'thing']
>>> testObject2.list
[1, 'thing', 'thing']
When you instantiate a class, __init__ method is automatically executed.
In the first case your list is a class attribute and is shared by all its instances. You got two 'thing's because you appended one when instantitating p and another when instantiated f (the first one was already appended at the first call).

Categories

Resources