Class level variable exposed via self in python - python

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.

Related

Can python classmethod change value of an instance variable and vice versa?

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

Methods in python are associated with the class. what does a function associated directly with the instance imply?

I know that in python, methods are associated the class and not the instance but they accept the instance as their first argument so as to access it. but, one can still define a function and tie it to an instance directly. What does this imply? and can this function somehow access the instance?
class TestClass:
def __init__(self, a):
self.a = a
def method1(self):
return self.a
my_obj = TestClass(12)
print(my_obj.method1()) // 12
Python also allows this:
class TestClass:
def __init__(self, a):
self.a = a
my_obj = TestClass(12)
def method1_defined_outside(self):
return self.a
TestClass.method1 = method1_defined_outside
print(my_obj.method1()) // 12
The two snippets above mean the same thing.
but python also allows this:
class TestCase:
def __init__(self, a):
self.a = a
my_obj = TestCase(12)
def method1_defined_outside(self):
return self.a
my_obj.method1 = method1_defined_outside
print(my_obj.method1()) // this throws an error stating the method1 is missing one postional argument(s), so clearly self does not automatically equate to the object
difference between the second and the third snippet is that the method defined outside is tied to the instance in latter, and to the class in the former.
When you access a method through an instance, but the method lives on the class, accessing the method actually creates a method wrapper. This wrapper knows what instance the method was called on, and passes it through to the actual function as the first argument (named, by convention, self). The mechanism behind this is the descriptor protocol and it's how properties work, too.
When you put the method directly on the instance, none of that applies and it's just a function that you access by way of the instance. It doesn't get self.
Now, you can create a method wrapper by manually invoking the descriptor, and set the resulting bound method on the instance.
my_obj.method1 = method1_defined_outside.__get__(my_obj)
Now my_obj.method1() passes my_obj as self.
The situations in which you'd want to set a method on an instance like this are fairly rare, though.
One way you could force sending the instance as self is by using the functools.partial wrapper as:
from functools import partial
class A:
def __init__(self):
self.x = 10
a1 = A()
def do_external(self):
print("EXTERNAL", self.x)
a1.doext = partial(do_external, a1)
a1.doext()
which will yield in the console:
EXTERNAL 10

What is the proper way of assigning values to object attributes in Python?

I am a complete Python novice so my question might seem to be dumb. I've seen there are two ways of assigning values to object attributes in Python:
Using __dict__:
class A(object):
def __init__(self,a,b):
self.__dict__['a'] = a
self.__dict__['b'] = b
Without __dict__:
class A(object):
def __init__(self,a,b):
self.a = a
self.b = b
Can anyone explain where is the difference?
Unless you need set attributes dynamically and bypass descriptors or the .__setattr__() hook, do not assign attributes directly to .__dict__.
Not all instances have a .__dict__ attribute even, not if the class defined a .__slots__ attribute to save memory.
If you do need to set attributes dynamically but don't need to bypass a descriptor or a .__setattr__() hook, you'd use the setattr() function normally.
From the Python documentation:
Both class types (new-style classes) and class objects
(old-style/classic classes) are typically created by class definitions
(see section Class definitions). A class has a namespace implemented
by a dictionary object. Class attribute references are translated to
lookups in this dictionary, e.g., C.x is translated to C.__dict__["x"]
(although for new-style classes in particular there are a number of
hooks which allow for other means of locating attributes).
The much more typical way of assigning a new element to a class is your second example, which allows for a number of customizations (__seattr__, __getattr__, etc.).

How should I choose between using instance vs. class attributes?

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

python: set a class variable programmatically before other class variables get set?

i have a case where i create a class inside an outer function and then return that class. the class has a specified parent class. i would like that class variable to be accessible by class methods on the parent class, these methods are called at class initialization time. in summary, i need to be able to set a class variable (not hardcoded) so that it is available before initializing other, hardcoded class variables.
here's some sample code to make that clearer:
class Parent(object):
class_var = None
#classmethod
def get_class_var_times_two(cls):
return cls.class_var * 2
def outer_function(class_var_value):
class Child(Parent):
other_var = Parent.get_class_var_times_two() # <-- at this point, somehow Child's class_var is set to class_var_value
Not sure if this is even possible in python. Maybe class_var_value doesn't need to be passed through the outer function. I tried using metaclasses and forcing the variable through in the class attribute dictinoary, but couldn't figure out how to set class_var on Child early enough so that it was set prior to initializing other_var. If that was possible, then this would all work. Any thoughts are appreciated!
Edit: also considered making other_var a lazy property, but that isn't an option for my use case.
Calling Parent.get_class_var_times_two() calls the function with cls = Parent, and so consequently the value of Parent.class_var will be used (regardless of what context you call the function from).
So, what you want to do is call Child.get_class_var_times_two(). Trouble is, Child doesn't get defined until the class block finishes. You therefore need to do something like this (assuming you don't use a metaclass):
def outer_function(class_var_value):
class Child(Parent):
class_var = class_var_value
Child.other_var = Child.get_class_var_times_two()

Categories

Resources