Difference between Class variables and Instance variables - python

I have already read many answers here on Stack Exchange like Python - why use "self" in a class?
After reading these answers, I understand that instance variables are unique to each instance of the class while class variables are shared across all instances.
While playing around, I found that this code which gives the output [1]:
class A:
x = []
def add(self):
self.x.append(1)
x = A()
y = A()
x.add()
print "Y's x: ", y.x
However, this code gives 10 as the output, when in my opinion it should be 11:
class A:
x = 10
def add(self):
self.x += 1
x = A()
y = A()
x.add()
print "Y's x: ", y.x
Why A class variable is not updated when I run x.add()? I am not very experienced in programming, so please excuse me.

Class variables are shadowed by instance attribute. This means that when looking up an attribute, Python first looks in the instance, then in the class. Furthermore, setting a variable on an object (e.g. self) always creates an instance variable - it never changes the class variable.
This means that when, in your second example you do:
self.x += 1
which is (in this case, see footnote) equivalent to:
self.x = self.x + 1
what Python does is:
Look up self.x. At that point, self doesn't have the instance attribute x, so the class attribute A.x is found, with the value 10.
The RHS is evaluated, giving the result 11.
This result is assigned to a new instance attribute x of self.
So below that, when you look up x.x, you get this new instance attribute that was created in add(). When looking up y.x, you still get the class attribute. To change the class attribute, you'd have to use A.x += 1 explicitly – the lookup only happens when reading the value of an attribute.
Your first example is a classical gotcha and the reason you shouldn't use class attributes as "default" values for instance attributes. When you call:
self.x.append(1)
there is no assignment to self.x taking place. (Changing the contents of a mutable object, like a list, is not the same as assignment.) Thus, no new instance attribute is added to x that would shadow it, and looking up x.x and y.x later on gives you the same list from the class attribute.
Note: In Python, x += y is not always equivalent to x = x + y. Python allows you to override the in-place operators separately from the normal ones for a type. This mostly makes sense for mutable objects, where the in-place version will directly change the contents without a reassignment of the LHS of the expression. However, immutable objects (such as numbers in your second example) do not override in-place operators. In that case, the statement does get evaluated as a regular addition and a reassignment, explaining the behaviour you see.
(I lifted the above from this SO answer, see there for more details.)

Related

Python 3 - Does the direct manipulation of a class' attribute override the same attribute for its objects making the attribue purely static?

While learning about how classes work in Python I came across a class definition example which behaved kind of strangely in my eyes.
The purpose of the example was to demonstrate how the behaviour of a static variable can be achieved in Python. The example was written as follows:
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
after setting up the class and creating the objects, I printed the values of the 'members' attribute. These were the results:
MemberCounter.members = 2
m1.members = 2
m2.members = 2
And that's when I got confused. While I was expecting for 'MemberCounter.members = 2' the two other results made no sense to me - why would both of 'm1' and 'm2' objects' 'members' value be equal to 2? I thought that both of the values should have been 0 - if the only attribute that was chaged is the 'members' attribute which was attached to the MemberCounter class why would it cause any change to the own unique 'members' value of each of the class' objects. It looks like the fact that the 'members' attribute is addresed like 'MemberCounter.members += 1' in the init() function of each object, completely overrides the unique values which m1.members and m2.members refer to and redirects their pointers to the MemberCounter.members value making all the three pointers point at the same value
==> m1.members = m2.members = MemberCounter.members.
Moreover, I have tried defining the class in an opossite way (Increasing self.members instead of MemberCounter.members):
class MemberCounter:
members = 0
def init(self):
self.members += 1
m1 = MemberCounter()
m1.init()
m2 = MemberCounter()
m2.init()
This definition yielded logical results (which got me curious about the above mentioned strange behaviour even more):
MemberCounter.members = 0
m1.members = 1
m2.members = 1
In short, I was curious about why the first class definition behaves in such a strange way? Why the mere 'MemberCounter.members += 1' statement completely erased 'm1.members' and 'm2.members' own unique value and made it equal to the MemberCounter.members value.
I hope I was able to clearly present my problem and I will be extremly happy to get an insight about this strange behaviour :)
That you can read a static attribute with instance.attribute notation as alternative to the more natural class.attribute notation, is an intended feature in Python.
From the documentation:
Both static data and static methods (in the sense of C++ or Java) are supported in Python.
For static data, simply define a class attribute. To assign a new
value to the attribute, you have to explicitly use the class name in
the assignment:
class C:
count = 0 # number of times C.__init__ called
def __init__(self):
C.count = C.count + 1
def getcount(self):
return C.count # or return self.count
c.count also refers to C.count for any c such that
isinstance(c, C) holds, unless overridden by c itself or by some
class on the base-class search path from c.__class__ back to C.
Caution: within a method of C, an assignment like self.count = 42
creates a new and unrelated instance named “count” in self’s own dict.
Rebinding of a class-static data name must always specify the class
whether inside a method or not:
C.count = 314
The paragraph just below the first code block explains your doubts. The "Caution" paragraph explains what you found logical.

Python: Keep changes on a variable made within a function

I have a question on a fairly simple task in python, however, I didn't manage to find a solution. I would like to assign a new value to an already existing variable in python. However, the changes I am doing to the variable within the function don't stick to the variable.
Here is a simplified example of my problem:
y = 1
x = None
def test(var):
var = y
return var
test(x)
print(x)
The print simply returns none. So the changes I have done to the variable within the function are non permanent.
How can I make the changes on the input-variable of the function permanent?
Thanks in advance!
Variables in Python are just names which refer to objects. In an expression, the name is a stand-in for the actual object. Saying test(x) means "pass the object referred to by x into test". It does not mean "pass the symbol x into test".
In addition, re-assigning a name only changes what object that name refers to. It affects neither the object nor any of its aliases.
In short, the name var you modify inside test has no relation to x at all.
The preferred way to have a function change something is by reassigning the result:
x = 2
def change(var):
return var * 2
x = change(x) # x now refers to 4 instead of 2
print(x)
If you want to change a name outside a function, you can use the nonlocal and global keywords:
x = 2
def change_x():
global x
x = x * 2
change_x() # x now refers to 4 instead of 2
print(x)
While this can make some trivial problems easy to solve, it is generally a bad idea for larger programs. Using global variables means one can no longer use the function in isolation; results may depend on how often and in what order such a function is called.
If you have some self-contained group of values and means to modify them, a class can be used to describe this:
class XY:
def __init__(self, x, y):
self.x, self.y = x, y
def swap(self):
self.x, self.y = self.y, self.x
my_values = XY(None, 1)
print(my_values.x, my_values.y)
my_values.swap()
print(my_values.x, my_values.y)
In contrast to global variables, you can create as many isolated instances of classes as needed. Each instance can be worked on in isolation, without affecting the others.
You can also use mutable values to make changes visible to the outside. Instead of changing the name, you modify the value.
x = [2] # x is a mutable list, containing the value 2 at the moment
def change(var):
var[0] = 4 # change leading element of argument
change(x) # x now contains 4 instead of 2
print(x)
This is an example of passing variables to functions by value. By default, when you pass a variable to a function in Python, it is passed by value.
What it means is, that a new variable with a new scope is created with the same value of x. So, any change that happens to the new x is not reflected to the x outside the function's scope.
If you want to get the value from the function back, you can use the return statement (as you have used). return returns the value back from the function. However, in your example there is no variable to receive it. Hence, it is lost.
You would have to call the function as x = test(x). This ensures that x receives the value back from the function.

Python - How to judge if input variable is duplicated in a function or not?

I've just get to know about Class and User-Defined-Function in Python and currently practicing with them.
Sometimes I got really confused whether an inputted variable or instance attribute is directly used or been copied as a local variable that only works during the call of function.
For example:
class test1(object):
def __init__(self, a):
self.a = a
class test2(object):
def __init__(self, test):
self.a = test.a
self.add_d()
def add_d(self):
self.a += 'd'
print self.a
class test3(object):
def __init__(self, test):
self.fc = test
self.add_d()
def add_d(self):
self.fc.a += 'd'
print self.fc.a
And:
In [36]: t = test1('abc')
In [37]: test2(t)
abcd
Out[37]: <__main__.test2 at 0xce5bcf98>
In [38]: t.a
Out[38]: 'abc'
It didn't change attribute "a" of instance "t" from class test1.
In [39]: test3(t)
abcd
Out[39]: <__main__.test3 at 0xfc3c9e8>
In [40]: t.a
Out[40]: 'abcd'
It changed attribute "a" of instance "t" from class test1.
Usually, I used functions for several scenario:
Aim to alter (mutate) the input variables from calling a function.
The return value is the only thing desired from the function call, and inputted variables should be unchanged.
Passing an Axes of matplotlib and arrange plotting commands in the function. (This would always work, never went wrong.)
Things would be pretty troublesome if I made unwanted changes to raw data and vice versa. So what is the basic concept here? Also, is there any habits that we should develop to prevent this kind of mistake?
I think that you are asking why was the test1 instance modified in the second example but not modified in the first?
In the first one after:
self.a = test.a
self.a refers to test.a which is the same immutable string object. Now when:
self.a += 'd'
executes it rebinds the value of the expression self.a + d to self.a, i.e. a new string object is created. Because strings are immutable the original can not be altered, so a new one must be created. At this point the attribute a in the test2 object refers to a different object than the a attribute in the test1 object, which remains unchanged.
In the second example after
self.fc = test
self.fc refers to the object test which is the same object that the variable t refers to. In terms of variable references this is the same as the first case, however, the difference is that this object is mutable so when
self.fc.a += 'd'
executes a new string is created and bound to the a attribute of the object - but self.fc still refers to the same object. Hence t.a is changed.
I've tried to explain this in terms of your example. The crucial point is that Python variables refer to objects, and there can be multiple references to the same object. Changes to the object can be made via any of the referring variables, and that change will be visible regardless of which variable is used to view it. Copies of objects are not made during assignment, however, the reference can be changed if a new object is created in order to preserve the immutability of certain objects.
I recommend that you give this a read Facts and myths about Python names and values.

How to use the += operator on a property that doesn't have a setter?

I'm getting a Can't set attribute error when I'm using the += operator on a read-only property that is of a type for which I've defined a __iadd__() method.
Simplified (but runnable) version of my code:
class Emitter(list):
def __iadd__(self, other):
self.append( other )
return self
class Widget:
def __init__(self):
self._on_mouseenter = Emitter()
#property
def on_mouseenter(self): return self._on_mouseenter
my_widget = Widget()
my_widget.on_mouseenter += lambda source: print("on_mouseenter!")
The last line produces the error. It goes away if I add the following line to the definition of Widget:
#on_mouseenter.setter
def on_mouseenter(self, value): pass
(Runnable at https://repl.it/EONf/0)
This behaviour seems strange on two accounts. First, I thought that Python passes objects by reference, so why should the property have to be readable? And second, how come that my dummy setter even works?
__iadd__ returns a replacement object to be rebound to the variable. This of course requires a setter.
In this case it works because you're ignoring the set, but still leaving the original object in place, which you've changed in place.
This behavior is required because some objects are immutable, but in place add still works on them.
i += 5 takes the number i is bound to, adds 5 to it, and rebinds i to the NEW result number. That is, it is exactly equivalent to i = i + 5, which has an assignment in it.
It's caused by how Python's augmented assignment operators work. After calling the appropriate special method, they assign the return value to the object at the left hand side of the operator.
If x is an instance of a
class with an __iadd__() method, x += y is equivalent to x = x.__iadd__(y). Otherwise, x.__add__(y) and y.__radd__(x) are
considered, as with the evaluation of x + y.
Therefore
my_widget.on_mouseenter += lambda source: print("on_mouseenter!")
is equivalent to
my_widget.on_mouseenter = my_widget.on_mouseenter.__iadd__(lambda source: print("on_mouseenter!"))
and you need a setter to perform assignment. It doesn't have to do anything, though, because the __iadd__ method is defined and modifies the list in-place.

When I instantiate a python subclass it overwrites base class' attribute

The code looks like:
class A(object):
x = 0
y = 0
z = []
def __init__(self):
super(A, self).__init__()
if not self.y:
self.y = self.x
if not self.z:
self.z.append(self.x)
class B(A):
x = 1
class C(A):
x = 2
print C().y, C().z
print B().y, B().z
The output is
2 [2]
1 [2]
Why is z overwritten but not y? Is it because it's not a immutable type? I looked on python's documentation and didn't find an explanation.
Yes, it's because one is immutable and one isn't. Or rather, it's that you are mutating one and not the other. (What matters isn't whether the object "is mutable", what matters is whether you actually mutate it.) It's also because you're using class variables instead of instance variables (see this question).
In your class definition, you create three class variables, shared among all instances of the class. After creating an instance of your class, if you do self.x on that instance, it will not find x as an attribute on the instance, but will look it up on the class. Likewise self.z will look up on the class and find the one on the class. Note that because you made z a class variable, there is only one list that is shared among all instances of the class (including all instances of all subclasses, unless they override z).
When you do self.y = self.x, you create a new attribute, an instance attribute, on only the instance.
However, when you do self.z.append(...), you do not create a new instance variable. Rather, self.z looks up the list stored on the class, and then append mutates that list. There is no "overwriting". There is only one list, and when you do the append you are changing its contents. (Only one item is appended, because you have if not self.z, so after you append one, that is false and subsequent calls do not append anything more.)
The upshot is that reading the value of an attribute is not the same as assigning to it. When you read the value of self.x, you may be retrieving a value that is stored on the class and shared among all instances. However, if you assign a value to self.x, you are always assigning to an instance attribute; if there is already a class attribute with the same name, your instance attribute will hide that.
The issue is that x and y are immutable, while z is mutable, and you mutate it.
self.z.append() does not replace z, it just adds an item to z.
After you run C() in print C().y, C().z (which is creating two different C objects), self.z no longer evaluates to False because it is no longer empty.
If you reverse your two print lines, you'll find the output is
1 [1]
2 [1]
When Python evaluates the body of class A it instantiates a single list object and assigns it to z. Since the subclasses don't override it, and since list objects are stored by reference in Python, all three classes share the same z list, so the first one you instantiate gets to populate z and then the rest just get whatever was put there. Although you changed the contents of the list, you did not change which list z refers to.
This does not affect y because you're assigning the integer directly into the object's internal dictionary, replacing the previous value.
To fix this, create your array inside the constructor, thus assigning:
class A(object):
x = 0
y = 0
z = None
def __init__(self):
super(A, self).__init__()
if not self.y:
self.y = self.x
if not self.z:
# create a new list
z = [self.x]
class B(A):
x = 1
class C(A):
x = 2
print C().y, C().z
print B().y, B().z

Categories

Resources