Python Class Attributes default - python

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.

Related

Python mutable class variable vs immutable class variable

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])

How to make list of object's attributes and change it from list?

I want to make a list of an object's attributes and modify them from a list.
Something like:
class new_class():
def __init__(self, number):
self.number = number
a = new_class(2)
b = [a.number]
b[0] = 1
# want it to be 1
print(a.number)
The simplest solution would be to store your instances in the list. There are other solutions that may require a designated class for your list, if you don't want to write b[0].number.
Storing the actual attribute in the list and changing the instance won't be possible, since you are storing variables of type int without any reference to the instance. You can however store an object containing a reference to the instance + the attribute, though I think storing the instance itself will be better.
class new_class():
def __init__(self, number):
self.number = number
a = new_class(2)
b = [a]
b[0].number = 1
# want it to be 1
print(a.number)
Maybe the nearest we can do is this:
class new_class():
def __init__(self, number):
self.number = number
a = new_class(2)
attrs = ['number']
def b(index, value):
a.__dict__[attrs[index]] = value
b(0, 1)
# want it to be 1
print(a.number)

How does accessing class variables from an instance work in python? - a confusing example

I have a very basic question about accessing class variables.
I thought I can reference class variables in a member function using the self keyword or the class name.
The code block below works along with my understanding
class Coo(object):
num = 1
def foo(self):
print self.num
print Coo.num
coo = Coo()
coo.foo()
Output:
1
1
The confusion starts with below example
class Coo(object):
num = 1
def foo(self):
self.num = 2
Coo.num = 3
print self.num
print Coo.num
coo = Coo()
coo.foo()
Output:
2
3
The second example shows that accessing class variable using self or class name are different.
What would be the right way to access class variables in a member function?
In python everything is an object, even classes themselves.
What:
class Coo(object):
num = 1
def foo(self):
print self.num
print Coo.num
does is that it creates a class object with the name Coo. It has attributes foo and num, with num being an int and foo being a function object.
coo = Coo()
Creates an instance object of Coo() that has the name coo. The instance coo contains no attributes:
print(coo.__dict__)
>>>{}
However, since you can do coo.foo(), or coo.num, coo clearly has attributes. The way coo gets the ability to use attributes from Coo is how python's attribute lookup works.
For example when doing coo.num, python attempts to look up num inside of coo.__dict__, but since it cannot find num, it moves into Coo.__dict__, and finds entry {num:10}
The same thing happens when you try to call coo.foo(), coo.__dict__ has no entry for foo, but Coo.__dict__ does. coo.foo() essentially becomes Coo.foo(coo), where the instance is passed in as self. This phenomenon is what lets instances of classes use their class functions! They look it up inside of their class's __dict__!
To use this to explain the anomaly in your question:
class Coo(object):
num = 1
def foo(self):
print self.num
print Coo.num
coo = Coo()
coo.foo()
coo has no num attribute, so num is looked up inside Coo and they both print 10.
class Coo(object):
num = 1
def foo(self):
self.num = 2
Coo.num = 3
print self.num
print Coo.num
coo = Coo()
coo.foo()
Here coo.__dict__ gains the entry {num:2} when self.num=2 is declared. Then inside of Coo.__dict__, num is set to 3.
self.num tries to look up num inside of coo.__dict__ and finds it, printing 2
Coo.num looks up num inside of Coo.__dict__ and finds it, printing 3.
As for the best way to access class variables in a member function, you should just use Classname.varname or self.__class__.varname. This guarantees that you won't end up using the instance variable with the same name. However, it is good design to have class and instance variables have different names. This way no confusion should ever occur.

Is self variable computed multiple times if functions are called more than once in Python?

I have a class where the shared variable self.a is obtained after a very heavy computation which requires a lot of time:
class MyClass(object):
def __init__(self):
# ----------------------------------
# function computationally demanding
out = demanding_function() # In this example, the output is a list of characters ['A','X','R','N','L']
# ----------------------------------
self.a = out
def fun1(self, string):
out = []
for letter in self.a:
out.append(string+letter)
return out
def fun2(self, number):
out = []
for letter in self.a:
out.append(str(number)+letter)
return out
o = MyClass()
x = o.fun1('Hello ')
y = o.fun2(2)
As you can see, self.a is used by the functions fun1 and fun2.
Here is my question is the following: if I call those 2 functions, is the demanding_function() executed multiple times or just once?
Note: this is a generic example and the variables don't have any specific meaning
The function is called just once, when the class instance is initialised i.e. when the __init__ of the class is called. Every other time you access self.a, the already assigned value is used; so no worries.
__init__ is only called once, when you instantiate the object. Any subsequent method calls using that instantiated object will use the already-computed values of instance varaibles

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

Categories

Resources