Can python classmethod change value of an instance variable?
And can normal methods change value of the class variable?
Any function in python can change any variable it has access to.
But that's a broad answer. Let's examine both of your questions in turn:
Can a class method change the value of an instance variable?
Consider the following class:
class A:
def __init__(self):
self.prop1 = 2
self.prop2 = "Beaver"
#classmethod
def do_something(cls):
...
If we're inside do_something, we don't know whether any specific instances exist. If you were to pass an instance of A into the method via an argument, then we could do whatever we wanted to that instance, but unless the instances of A are stored somewhere we can see, you can't modify them from within.
One workaround to this is keeping a list of instances in a class variable:
class A2:
instances = []
def __init__(self):
# setting instance variables
self.prop1 = 2
self.prop2 = "Beaver"
# setting class variables
A2.instances.append(self)
#classmethod
def do_something(cls):
...
This time, we would be able to access an instance from within do_something, because that instance would be somewhere in instances, which is a class variable.
Can instance methods change the value of class variables?
Yes, because any instance method is necessarily going to have access to the class of the instance. You can write this explicitly, as we did above in class A2, or if you were dealing with polymorphism you could access the __class__ attribute of the instance:
class B(A2):
def __init__():
self.prop1 = 7
self.prop2 = "Chihuaua"
self.__class__.instances.append(self)
The principle "you can change anything you can see" is fun and dangerous to mess around with. For example, you can redefine built-in methods, and you can even change class methods from outside of them (here, I will redefine the __init__() method of the class B outside of the class itself - now all new instances of B will use my constructor instead of the one B initially came with):
def my_new_init(self):
self.prop1 = 10
self.prop2 = "bear"
B.__init__ = my_new_init
It's extremely dangerous to go down this rabbit hole unless you know exactly what you're doing.
Can python classmethod change value of an instance variable?
No. Class object does not have direct access to any instance variable.
can normal methods change value of the class variable?
Yes; but the change will be on it's own view (unless the name is referred by using class explicitly e.g. by name or self.__class__) i.e. it won't change the class variable as seen by the class or any other object. If the class object is referred explictly the class variable will be changed as you can imagine.
No and Yes.
A classmethod has no access to the instance object.
While an instance method can access the class object via self.__class__.
It's however a bad idea to change a value that's global for a class in an instance method because that's an unexpected side effect.
Class-level attributes are transparently accessible via self just like instance-specific ones. So if you need to override a class-level attribute for a specific instance, just assign it in self:
In [1]: class C(object):
...: ca=1
...: cb=1
...: def inc_ca(self):
...: self.ca+=1
...:
In [2]: c=C()
In [4]: c.ca
Out[4]: 1
In [5]: c.ca=2
In [6]: c.ca
Out[6]: 2
In [7]: c.inc_ca()
In [8]: c.ca
Out[8]: 3
In [9]: C.ca
Out[9]: 1
Related
I need to change object variable directly from inherited class.
Here is my code example:
class A(object):
def __init__(self,initVal=0):
self.myVal = initVal
def worker(self):
self.incrementor = B()
self.incrementor.incMyVal(5)
class B(A):
def incMyVal(self,incVal):
super().myVal += incVal
obj = A(5)
print(obj.myVal)
obj.worker()
print(obj.myVal)
But it doesn't work:
AttributeError: 'super' object has no attribute 'myVal'
I also tried to use global/nonlocal keywords with my variable in B class, but no luck.
In my main case, the B class is an event handler. And it should change the attribute of an object when an event fires. So I'm not able to use return in the incMyVal method.
super() can only search for class attributes in the class MRO, not instance attributes. myVal is set on an instance of the class, not on a class itself.
There is only ever one instance; it doesn't matter if code from class A or a derived class is altering attributes on an instance, it is just one namespace.
However, in your case, you shouldn't even be using inheritance. You are trying to use an independent, second instance to alter the attributes of an instance of A. Class inheritance doesn't give you access to instances of the base class like this.
Refactor B to take an instance of A, then act on that instance:
class B:
def __init__(self, ainstance):
self.ainstance = ainstance
def incMyVal(self, incVal):
self.ainstance.myVal += incVal
Note that B is not a subclass of A here; it is not a (specialised) object of the same type at all; it is a different kind of thing, something that increments attributes of another object.
Pass in the instance when you create an instance of B:
def worker(self):
self.incrementor = B(self)
self.incrementor.incMyVal(5)
This does create a circular reference, which can keep objects alive for longer than perhaps needed. You may want to use a weak reference instead:
import weakref
class B:
def __init__(self, ainstance):
self.ainstance_ref = weakref.ref(ainstance)
def incMyVal(self, incVal):
ainstance = self.ainstance_ref()
if ainstance is not None:
ainstance.myVal += incVal
Now B instances only hold a weak reference to their A instance, and will do nothing if that instance no longer exists.
class ClassA:
A = 10
def __init__(self):
self.a = 10
self.b = 20
def methodA(self):
return self.A
obj = ClassA()
print(obj.methodA()) # returns 10
This clearly has limitation wherein, I obj will not have an attribute by name "A". But why did python exposes the 'A' via self.A. Is it better to return it as ClassA.A instead?
This is entirely by design.
Methods on classes are attributes on the class too (albeit enhanced by being descriptors as well). This is how self.methodA() would be located too.
Python searches attributes first on the instance, then the class, then the base classes of the class.
Note that setting self.A will still set the value on the instance, thereby masking the class attribute. This lets classes specify default values for attributes, to be replaced by values on the instance later on.
This all doesn't preclude you from accessing ClassA.A directly and bypass the instance dictionary. If you wanted to change the shared value, you'd have to assign directly to ClassA.A for the above reason.
I was wondering what was the difference between the Foo.var= user input and self.var= userinput in the 2 classes.
class foo():
var=None
def __init__(self,userinput):
foo.var=userinput
class bar():
var=None
def __init__(self,userinput):
self.var=userinput
foo refers to the class, self refers to the object.
Class members are a property of the class (and thus are shared between all objects of that class), while instance members are a property of the specific object, so a change to an instance member affects only the given object.
When you operate on an object, the members it has are a merge of the class members and the instance members. When two members with the same name are defined, the instance members have the priority.
Thus:
bar sets an instance variable; that change has effect only on the current instance, so if you do:
b=bar(10)
c=bar(20)
you'll see that c.var is 20 and b.var is 10; nothing strange here;
foo sets a class variable, which is common to all the instances; so, if you do:
f=foo(10)
g=foo(20)
you'll see that both f.var and g.var will be 20, because they both actually refer to foo.var, that was last set to 20 in g's constructor;
on the other hand, instance variables shadow class variables; so, if you do
f=foo(10)
g=foo(20)
f.var=30
you'll have g.var==foo.var==20, but f.var==30, since now f.var refers to the instance variable f.var; but, if you do
del f.var
now the instance (f's) attribute var no longer exists, and thus f.var refers again to the class attribute var (thus f.var==g.var==foo.var==20).
Long story short: normally you'll want to use self.var (i.e. instance members); classname.var is only for sharing stuff between all instances of a given class.
I'd like to point to an existing post which explains the difference perfectly in my opinion.
Python: Difference between class and instance attributes
Yes,
In the first instance you are setting the variable for all instances of foo this is because it is a class variable.
In the second case you are only setting the variable for that instance of foo.
For Example:
class pie():
def __init__(self, j):
pie.var = "pies" + str(j)
print (self.var)
def __str__(self):
return self.var
a = pie(1)
b = pie(2)
print (a)
print (b)
I tried this example code:
class testclass:
classvar = 'its classvariable LITERAL'
def __init__(self,x,y):
self.z = x
self.classvar = 'its initvariable LITERAL'
self.test()
def test(self):
print('class var',testclass.classvar)
print('instance var',self.classvar)
if __name__ == '__main__':
x = testclass(2,3)
I need some clarification. In both cases, I'm able to access the class attribute and instance in the test method.
So, suppose if I have to define a literal that needs to be used across all function, which would be the better way to define it: an instance attribute or a class attribute?
I found this in an old presentation made by Guido van Rossum in 1999 ( http://legacy.python.org/doc/essays/ppt/acm-ws/sld001.htm ) and I think it explains the topic beautifully:
Instance variable rules
On use via instance (self.x), search order:
(1) instance, (2) class, (3) base classes
this also works for method lookup
On assigment via instance (self.x = ...):
always makes an instance variable
Class variables "default" for instance variables
But...!
mutable class variable: one copy shared by all
mutable instance variable: each instance its own
Class variables are quite good for "constants" used by all the instances (that's all methods are technically). You could use module globals, but using a class variable makes it more clearly associated with the class.
There are often uses for class variables that you actually change, too, but it's usually best to stay away from them for the same reason you stay away from having different parts of your program communicate by altering global variables.
Instance variables are for data that is actually part of the instance. They could be different for each particular instance, and they often change over the lifetime of a single particular instance. It's best to use instance variables for data that is conceptually part of an instance, even if in your program you happen to only have one instance, or you have a few instances that in practice always have the same value.
It's good practice to only use class attributes if they are going to remain fixed, and one great thing about them is that they can be accessed outside of an instance:
class MyClass():
var1 = 1
def __init__(self):
self.var2 = 2
MyClass.var1 # 1 (you can reference var1 without instantiating)
MyClass.var2 # AttributeError: class MyClass has no attribute 'var2'
If MyClass.var is defined, it should be the same in every instance of MyClass, otherwise you get the following behaviour which is considered confusing.
a = MyClass()
b = MyClass()
a.var1, a.var2 # (1,2)
a.var1, a.var2 = (3,4) # you can change these variables
a.var1, a.var2 # (3,4)
b.var1, b.var2 # (1,2) # but they don't change in b
MyClass.var1 # 1 nor in MyClass
You should define it as a class attribute if you want it to be shared among all instances. You should define it as an instance variable if you want a separate one for each instance (e.g., if different instances might have different values for the variable).
Why are constructors indeed called "Constructors"? What is their purpose and how are they different from methods in a class?
Also, can there be more that one __init__ in a class? I tried the following, can someone please explain the result?
>>> class test:
def __init__(self):
print "init 1"
def __init__(self):
print "init 2"
>>> s=test()
init 2
Finally, is __init__ an operator overloader?
There is no function overloading in Python, meaning that you can't have multiple functions with the same name but different arguments.
In your code example, you're not overloading __init__(). What happens is that the second definition rebinds the name __init__ to the new method, rendering the first method inaccessible.
As to your general question about constructors, Wikipedia is a good starting point. For Python-specific stuff, I highly recommend the Python docs.
Why are constructors indeed called "Constructors" ?
The constructor (named __new__) creates and returns a new instance of the class. So the C.__new__ class method is the constructor for the class C.
The C.__init__ instance method is called on a specific instance, after it is created, to initialise it before being passed back to the caller. So that method is the initialiser for new instances of C.
How are they different from methods in a class?
As stated in the official documentation __init__ is called after the instance is created. Other methods do not receive this treatment.
What is their purpose?
The purpose of the constructor C.__new__ is to define custom behaviour during construction of a new C instance.
The purpose of the initialiser C.__init__ is to define custom initialisation of each instance of C after it is created.
For example Python allows you to do:
class Test(object):
pass
t = Test()
t.x = 10 # here you're building your object t
print t.x
But if you want every instance of Test to have an attribute x equal to 10, you can put that code inside __init__:
class Test(object):
def __init__(self):
self.x = 10
t = Test()
print t.x
Every instance method (a method called on a specific instance of a class) receives the instance as its first argument. That argument is conventionally named self.
Class methods, such as the constructor __new__, instead receive the class as their first argument.
Now, if you want custom values for the x attribute all you have to do is pass that value as argument to __init__:
class Test(object):
def __init__(self, x):
self.x = x
t = Test(10)
print t.x
z = Test(20)
print t.x
I hope this will help you clear some doubts, and since you've already received good answers to the other questions I will stop here :)
Classes are simply blueprints to create objects from. The constructor is some code that are run every time you create an object. Therefor it does'nt make sense to have two constructors. What happens is that the second over write the first.
What you typically use them for is create variables for that object like this:
>>> class testing:
... def __init__(self, init_value):
... self.some_value = init_value
So what you could do then is to create an object from this class like this:
>>> testobject = testing(5)
The testobject will then have an object called some_value that in this sample will be 5.
>>> testobject.some_value
5
But you don't need to set a value for each object like i did in my sample. You can also do like this:
>>> class testing:
... def __init__(self):
... self.some_value = 5
then the value of some_value will be 5 and you don't have to set it when you create the object.
>>> testobject = testing()
>>> testobject.some_value
5
the >>> and ... in my sample is not what you write. It's how it would look in pyshell...
coonstructors are called automatically when you create a new object, thereby "constructing" the object. The reason you can have more than one init is because names are just references in python, and you are allowed to change what each variable references whenever you want (hence dynamic typing)
def func(): #now func refers to an empty funcion
pass
...
func=5 #now func refers to the number 5
def func():
print "something" #now func refers to a different function
in your class definition, it just keeps the later one
There is no notion of method overloading in Python. But you can achieve a similar effect by specifying optional and keyword arguments