Object vs. class variable - python

This is a completely theoretical question. Suppose the following code:
>>> class C:
... a = 10
... def f(self): self.a = 999
...
>>>
>>> C.a
10
>>> c = C()
>>> c.a
10
>>> c.f()
>>> c.a
999
At this point, is class variable C.a still accessible through the object c?

Yes, though c.__class__.a or type(c).a. The two differ slightly in that old-style classes (hopefully, those are all dead by now - but you never know...) have a type() of <type 'instance'> (and __class__ works as expected) while for new-style classes, type() is identical to __class__ except when the object overrides attribute access.

All class variables are accessible through objects instantiated from that class.
>>> class C:
... a = 10
... def f(self): self.a = 999
...
>>> C.a
10
>>> c = C()
>>> c.a
10
>>> c.f()
>>> c.a
999
>>> c.__class__.a
10
>>> c.a
999
>>> del(c.a)
>>> c.a
10
Attributes are first searched within the object namespace and then class.

Yes, you can access a from an object c, à la c.a. The value would initially be 10.
However, if you call c.f(), the value of c.a will now be 999, but C.a will still be 10. Likewise, if you now change C.a to, say, 1000, c.a will still be 999.
Basically, when you instantiate an instance of C, it will use the class variable as its own a value, until you change the value of that instance's a, in which case it will no longer "share" a with the class.

After you assign to it on the class instance, there is both a class attribute named a and an instance attribute named a. I illustrate:
>>> class Foo(object):
... a = 10
...
>>> c = Foo()
>>> c.a
10
>>> c.a = 100 # this doesn't have to be done in a method
>>> c.a # a is now an instance attribute
100
>>> Foo.a # that is shadowing the class attribute
10
>>> del c.a # get rid of the instance attribute
>>> c.a # and you can see the class attribute again
10
>>>
The difference is that one exists as an entry in Foo.__dict__ and the other exists as an entry in c.__dict__. When you access instance.attribute, instance.__dict__['attribute'] is returned if it exists and if not then type(instance).__dict__['attribute'] is checked. Then the superclasses of the class are checked but that gets slightly more complicated.
But at any rate, the main point is that it doesn't have to be one or the other. A class and an instance can both have distinct attributes with identical names because they are stored in two separate dicts.

Related

How does type() function infers type

Im trying to compare types of two objects by using isinstance(obj1, type(obj2)).
However, im not really sure how type infers the typing of an object - or whether theres a chance that the type returned is of an ancestor class.
Given three classes A, B, C. Class B is subclass of A and class C is subclass of B.
class A:
pass
class B(A):
pass
class C(B):
pass
Using type on an object is the same thing as calling obj.__class__. This returns the class to which an instance belongs.
isinstance however also checks for subclasses. So your call isinstance(obj1, type(obj2)) depends on if the two objects are related.
>>> a = A()
>>> b = B()
>>> c = C()
>>> type(c) == type(b)
False
>>> isinstance(c, type(b))
True
Instance c is of type <class '__main__.C'> and b is of type <class '__main__.B'>. So the comparison using type evaluates to False.
A more elaborate example of your approach and using the class directly:
>>> isinstance(c, type(b)) # as C is subclass of B
True
>>> isinstance(c, B) # using the class directly
True
>>> isinstance(b, type(c)) # as b is of parent class B
False
>>> isinstance(b, C) # C is subclass of B, thus b is not an instance of C
False
>>> isinstance(c, type(a)) # C is subclass of B which is subclass of A
True

How to get the id of the underlying function bound to an object by a method-wrapper?

Consider the following case:
>>> "a".capitalize.__call__
<method-wrapper '__call__' of builtin_function_or_method object at 0x1022e70d8>
>>> "a".capitalize.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x1022e2b70>
>>> id("a".capitalize.__call__)
4331547448
>>> id("a".capitalize.__call__.__call__)
4331547504
>>> id("a".capitalize.__call__) == id("a".capitalize.__call__.__call__)
False
How can I establish at runtime that the __call__ refers to the same base symbol in both cases ?
Edit:
It is possible that expecting something like this to exist for __call__ in the first place is unreasonable because __call__ might not have a base symbol that is not bound to any object - in which case what is the best way to detect that other than keeping a list of special names (how can such a list be built authoritatively ?) ?
Won't work for builtin_function_or_method as they have no __func__.
If you're only working with your classes you can compare __func__:
>>> class Foo:
... def bar(self):
... pass
...
>>>
>>> a = Foo()
>>> b = Foo()
>>> a.bar.__func__
<function Foo.bar at 0x7f6f103e5ae8>
>>> a.bar.__func__ is b.bar.__func__
True
But I'd say it's better to do it the other way around: instead of trying to get the function from the instance, get up to the type first:
type(a).bar is type(a).bar
Which should work even for bltns:
>>> type("").capitalize is type("a").capitalize
True
And for hierarchies:
>>> class A:
... def foo(self):...
...
>>> class B(A):...
...
>>> a = A()
>>> b = B()
>>> type(a).foo is type(b).foo
True

Understanding objects relationship in python (confusion) [duplicate]

This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 8 years ago.
I am a little bit confused by this example:
>>> class A:
... foo = []
>>> a, b = A(), A()
>>> a
<__main__.A instance at 0x0000000002296A88>
>>> b
<__main__.A instance at 0x0000000002296F88>
>>> a.foo.append(5)
>>> a.foo
[5]
>>> b.foo
[5]
1) How does python connect two different instances?
2) Does the instance refer to a class A() or foo attribute after appending the value?
But when i add __init__ method, things look different:
>>> class A:
... def __init__(self):
... self.foo = []
...
>>> a, b = A(), A()
>>> a
<__main__.A instance at 0x00000000021EC508>
>>> b
<__main__.A instance at 0x0000000002296F88>
>>> a.foo.append(5)
>>> a.foo
[5]
>>> b.foo
[]
3) What is the magic of __init__ ?
In the first case, the foo = [] is done at class definition time, and thus a single list is associated with the class, rather than the instance.
In the second case, the self.foo = [] is done at instance initialization time (which is what __init__ is - instance initialization), and thus a separate list is associated with each instance.
In your first example, foo is a class attribute, not an instance attribute. This means it's shared across all the instances of A, which you can check with:
a1 = A()
a2 = A()
print a1.foo is a2.foo
print a1.foo is A.foo
In your second example, however, self.foo = [] makes foo an instance attribute, built independently for each instance of A.

Python class variables or class variables in general

From Dive into Python:
Class attributes are available both through direct reference to the
class and through any instance of the class.
Class attributes can be used as class-level constants, but they are
not really constants. You can also change them.
So I type this into IDLE:
IDLE 2.6.5
>>> class c:
counter=0
>>> c
<class __main__.c at 0xb64cb1dc>
>>> v=c()
>>> v.__class__
<class __main__.c at 0xb64cb1dc>
>>> v.counter += 1
>>> v.counter
1
>>> c.counter
0
>>>
So what am I doing wrong? Why is the class variable not maintaining its value both through direct reference to the class and through any instance of the class.
Because ints are immutable in python
v.counter += 1
rebinds v.counter to a new int object. The rebinding creates an instance attribute that masks the class attribute
You can see this happening if you look at the id() of v.counter
>>> id(v.counter)
149265780
>>> v.counter+=1
>>> id(v.counter)
149265768
Here you can see that v now has a new attribute in its __dict__
>>> v=c()
>>> v.__dict__
{}
>>> v.counter+=1
>>> v.__dict__
{'counter': 1}
Contrast the case where counter is mutable, eg a list
>>> class c:
... counter=[]
...
>>> v=c()
>>> v.counter+=[1]
>>> c.counter
[1]
>>>
Before:
c.counter = 0
v.counter -> c.counter
During:
c.counter = 0
v.counter = c.counter + 1
After:
c.counter = 0
v.counter = 1
Note that you can still get at the class value:
v.__class__.__dict__['counter']
will allow you to read or set to your class, even if you have obscured the symbol by adding a symbol to your instance's __dict__.
Your are confused between declaration and instantiation.
C is the name of a class you declared.
v is an object, instantiated from c.

Python and object/class attrs - what's going on?

Can someone explain why Python does the following?
>>> class Foo(object):
... bar = []
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]
>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]
>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]
It's rather confusing!
This is because the way you have written it, bar is a class variable rather than an instance variable.
To define an instance variable, bind it in the constructor:
class Foo(object):
def __init__(self):
self.bar = []
Note that it now belongs to a single instance of Foo (self) rather than the Foo class, and you will see the results you expect when you assign to it.
As others have said the code as written creates a class variable rather than an instance variable. You need to assign in __init__ to create an instance variable.
Hopefully this annotated copy of your code is helpful in explaining what's going on at each stage:
>>> class Foo(object):
... bar = [] # defines a class variable on Foo (shared by all instances)
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1) # appends the value 1 to the previously empty list Foo.bar
>>> b.bar # returns the value of the class variable Foo.bar
[1]
>>> a.bar = 1 # binds 1 to the instance variable a.bar, masking the access
>>> a.bar # you previously had to the class variable through a.bar
1
>>> b.bar # b doesn't have an instance variable 'bar' so this still
[1] # returns the class variable
>>> a.bar = [] # bind a's instance variable to to an empty list
>>> a.bar
[]
>>> b.bar # b doesn't have an instance variable 'bar' so this still
[1] # returns the class variable
>>> del a.bar # unbinds a's instance variable unmasking the class variable
>>> a.bar # so a.bar now returns the list with 1 in it.
[1]
Also, printing out the value of Foo.bar (the class variable accessed via the class rather than via an instance) after each of your statements might help clarify what is going on.
When you declare an element in the class like that it is shared by all instances of the class. To make a proper class member that belongs to each instance, separately, create it in __init__ like the following:
class Foo(object):
def __init__(self):
self.bar = []
In the beginning, bar is a class variable and it is shared between a and b, both a.bar and b.bar refer to the same object.
When you assign a new value to a.bar, this does not overwrite the class variable, it adds a new instance variable to the a object, hiding the class variable when you access a.bar. If you delete a.bar (the instance variable), then a.bar resolves again to the class variable.
b.bar on the other hand always refers to the class variable, it's not influenced by the additional bar on the a object or any values assigned to that.
To set the class variable you can access it through the class itself:
Foo.bar = 1
>>> class Foo(object):
... bar = []
...
bar is a shared class variable, not an instance variable. I believe that deals with most of your confusion. To make it a instance var, define it in class's __init__ per the other answers.
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]
This is the proof of that.
>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]
Now you've redefined a.bar as a instance variable. That's what happens when you define variables externally by default.
>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]
Same again. b.bar is still the shared class variable.
On a related note, you should be aware of this pitfall that you might see sometime soon:
class A:
def __init__(self, mylist = []):
self.mylist = mylist
a = A()
a2 = A()
a.mylist.append(3)
print b.mylist #prints [3] ???
This confuses a lot of folks and has to do with how the code is interpreted. Python actually interprets the function headings first, so it evaluates __init__(self, mylist = []) and stores a reference to that list as the default parameter. That means that all instances of A will (unless provided their own list) reference the original list. The correct code for doing such a thing would be
class A:
def __init__(self, mylist=None):
if mylist:
self.mylist = mylist
else:
self.mylist = []
or if you want a shorter expression you can use the ternary syntax:
self.mylist = mylist if mylist else []

Categories