Binding a method to another class - python

class A(object):
def a(self, b=1):
print 'Up'
d = {1 : a}
def b( self ):
print self.d[1]
print self.b
print self.d[1].__get__( self, A )()
# print self.d[1]()
class B( object ):
def a( self ):
print 'here??'
return 10000
d = {1 : a}
def b( self ):
print 'hurray'
o = A()
o.b()
b = B()
type( o ).__dict__['b'].__get__( b, type( b ) )()
Hi Folks,
I was going through Python: Bind an Unbound Method? and http://users.rcn.com/python/download/Descriptor.htm and trying to experiment on my learning.
But, I have hit some new doubts now:-
In the last line of my code, I'm able to use __get__ with b object and instance: type(b). This only works if method b is defined in class B. Why is it so?
Even though the last line requires me to provide a method b in class B, still the method b in class A gets called. Why is it so?
To my utter surprise, after the above step, I notice that the method a of class A is not called by the code of method b of class A; instead, it calls the method a of class B. Why is it so?
I'm quite confused after seeing this behaviour. I might also need to learn more on descriptors. But, it would be a great help if you could answer my doubts

In the last line of my code, I'm able to use __get__ with b object and instance: type(b). This only works if method b is defined in class B. Why is it so?
You have to define a method b in class B, because in A.b you have print self.b. Here, self is an instance of the B class, so self.b means "the b method belonging to this B", not "the b method belonging to the class that this method exists in". If you delete print self.b, then the code will work even if B has no b.
Even though the last line requires me to provide a method b in class B, still the method b in class A gets called. Why is it so?
A.b is being called because you are explicitly accessing it with type( o ).__dict__['b']. Whether you bind that method to an A instance or a B instance doesn't matter; it's still A.b.
To my utter surprise, after the above step, I notice that the method a of class A is not called by the code of method b of class A; instead, it calls the method a of class B. Why is it so?
Even though b belongs to the class A, the self you pass to it is still an instance of the B class. Any attributes you access on that self will be B attributes, and any methods you call on it will be B methods.

Related

Why does the MRO of this class change when one subclass does/doesnt inherit a third class in Python?

I'm trying to get a better understanding of MRO in Python & came across this example:
class A:
def process(self):
print('A process()')
class B(A):
pass
class C(A):
def process(self):
print('C process()')
class D(B,C):
pass
obj = D()
obj.process()
which prints "C process()". I understand why, because the order goes D>B>C>A. but, when the class C doesn't inherit A, then "A process()" is printed & the order shifts to D>B>A>C. what causes the order to shift here? why isn't the C superclass reached before the A class now?
The C3 linearization algorithm is somewhat depth-first, so A, being reachable from B (which is listed before C in the base class list) is added before C.
The rationale is that D is more "B-like" than "C-like", so anything that is part of "B" should appear before "C".
(For fun, see what happens if you try something like class D(B, A, C) when C still inherits from A.)

How do I know how python core is working?

How do I know how the core python will work.
For ex see the below code:
class A(object):
def a(self):
print 111111
def b(self):
self.a()
class B(A):
def a(self):
print 222222
When I do
a = B()
a.b()
it is printing
222222
What could be the reason why it did not print "11111", if you say 'self' is the object of class B, so its calling its own method, but where it is defined that 'self' is the object of class B?
Where do I see the internal logic that's calling 'a' method from class B?
Every time I came across this logic, I forget and assumes that it prints '111111', so is there any way I can check the internal python behavior?
You are assuming that self.a refers to A.a just because it occurs in the definition of A.b. That's not true; the behavior of self.a is determined by the runtime type of self.
Since a is an instance of B, a.b() is the same as type(a).b(a, b) or B.b(a, b). Since B.b is not defined, the attribute lookup process falls back to A.b. Inside A.b, self == a, so we now have a.a(), which again is equivalent to type(a).a(a) == B.a(a). As a result, A.a never gets called.

Python assignment to self in constructor does not make object the same

I am making a constructor in Python. When called with an existing object as its input, it should set the "new" object to that same object. Here is a 10 line demonstration:
class A:
def __init__(self, value):
if isinstance(value, A):
self = value
else:
self.attribute = value
a = A(1)
b = A(a)#a and b should be references to the same object
print("b is a", b is a)#this should be true: the identities should be the same
print("b == a", b == a)#this should be true: the values should be the same
I want the object A(a) constructed from the existing object a to be a. Why is it not? To be clear, I want A(a) to reference the same object as a, NOT a copy.
self, like any other argument, is among the local variables of a function or method. Assignment to the bare name of a local variable never affects anything outside of that function or method, it just locally rebinds that name.
As a comment rightly suggests, it's unclear why you wouldn't just do
b = a
Assuming you have a sound reason, what you need to override is not __init__, but rather __new__ (then take some precaution in __init__ to avoid double initialization). It's not an obvious course so I'll wait for you to explain what exactly you're trying to accomplish.
Added: having clarified the need I agree with the OP that a factory function (ideally, I suggest, as a class method) is better -- and clearer than __new__, which would work (it is a class method after all) but in a less-sharply-clear way.
So, I would code as follows:
class A(object):
#classmethod
def make(cls, value):
if isinstance(value, cls): return value
return cls(value)
def __init__(self, value):
self.attribute = value
Now,
a = A.make(1)
b = A.make(a)
accomplishes the OP's desires, polymorphically over the type of argument passed to A.make.
The only way to make it work exactly as you have it is to implement __new__, the constructor, rather than __init__, the initialiser (the behaviour can get rather complex if both are implemented). It would also be wise to implement __eq__ for equality comparison, although this will fall back to identity comparison. For example:
>>> class A(object):
def __new__(cls, value):
if isinstance(value, cls):
return value
inst = super(A, cls).__new__(cls)
inst.attribute = value
return inst
def __eq__(self, other):
return self.attribute == other.attribute
>>> a = A(1)
>>> b = A(a)
>>> a is b
True
>>> a == b
True
>>> a == A(1)
True # also equal to other instance with same attribute value
You should have a look at the data model documentation, which explains the various "magic methods" available and what they do. See e.g. __new__.
__init__ is an initializer, not a constructor. You would have to mess around with __new__ to do what you want, and it's probably not a good idea to go there.
Try
a = b = A(1)
instead.
If you call a constructor, it's going to create a new object. The simplest thing is to do what hacatu suggested and simply assign b to a's value. If not, perhaps you could have an if statement checking if the value passed in is equal to the object you want referenced and if it is, simply return that item before ever calling the constructor. I haven't tested so I'm not sure if it'd work.

super confusing python multiple inheritance super()

I was playing around with the multiple inheritance in python and I come a cross a situation that I can't understand how it happen.
Here is the inheritance layout:
A F
/ \ |
B C |
\ | /
\ | /
D
The ABCD diamond that everyone familiar with.
Plus an extra class "F" I throw it in for fun.
Here is the code:
class A(object):
def foo(self, call_from):
print "foo from A, call from %s" % call_from
super(A, self).foo("A")
class B(A):
def foo(self, call_from):
print "foo from B, call from %s" % call_from
super(B, self).foo("B")
class C(A):
def foo(self, call_from):
print "foo from C, call from %s" % call_from
super(C, self).foo("C")
class F(object):
def foo(self, call_from):
print "foo from F, call from %s" % call_from
class D(B, C, F):
def foo(self):
print "foo from D"
super(D, self).foo("D")
output:
>>> d = D()
>>> d.foo()
foo from D
foo from B, call from D
foo from C, call from B
foo from A, call from C
foo from F, call from A
The method resolution order:
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.F'>, <type 'object'>)
foo from C, call from B instead of foo from C, call from D
foo from F, call from A just simply throw me off...
It seems like the super() are chained up according to the method resolution order and ignore the relationship between classes, but I not sure.
Can someone point me to the right direction to understand this behavior?
Please keep in mind that I'm trying to understand the language itself. Not trying to solve a practical problem. So I don't have an use case for this. But it will be nice if someone can point out an use case :)
UPDATE:
To summarize - super() simply let you know what is next to call base on the mro. It is not necessary the parent. While mro built base on the inheritance hierarchy, mro itself is not the inheritance hierarchy.
The whole point of super() is to follow the method resolution order. That's why you tell it your own class, not your parent class. It's hard for the programmer to predict which class will be invoked next, so you let super() take care of it.
You already had B called from D, so how could you then get C called from D? D.foo() can only call one other foo(), because you only have one function call there. It's going to be a linear chain of calls, so the classes have to be linearized, that's what the method resolution order does.
Occasionally I find it useful to call super on parent class. Ex.
class TmpClass0(object):
def tmp_method(self):
print 'TmpClass0 tmp_method'
class TmpClass1(TmpClass0):
def tmp_method(self):
print 'TmpClass1 tmp_method'
Now I want to use TmpClass0's tmp_method from an instance of TmpClass2.
class TmpClass2(TmpClass1):
def tmp_method(self):
super(TmpClass1, self).tmp_method()
Result:
In [107]: tmp_class2 = TmpClass2()
In [108]: tmp_class2.tmp_method()
TmpClass0 tmp_method

Apply a method to an object of another class

Given two non-related classes A and B, how to call A.method with an object of B as self?
class A:
def __init__(self, x):
self.x = x
def print_x(self):
print self.x
class B:
def __init__(self, x):
self.x = x
a = A('spam')
b = B('eggs')
a.print_x() #<-- spam
<magic>(A.print_x, b) #<-- 'eggs'
In Python 3.x you can simply do what you want:
A.print_x(b) #<-- 'eggs'
If you only have an instance of 'A', then get the class first:
a.__class__.print_x(b) #<-- 'eggs'
In Python 2.x (which the OP uses) this doesn't work, as noted by the OP and explained by Amber in the comments:
This is a difference between Python 2.x and Python 3.x - methods in
3.x don't enforce being passed the same class.
More details (OP edit)
In python 2, A.print_x returns an "unbound method", which cannot be directly applied to other classes' objects:
When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof. >> http://docs.python.org/reference/datamodel.html
To work around this restriction, we first have to obtain a "raw" function from a method, via im_func or __func__ (2.6+), which then can be called passing an object. This works on both classes and instances:
# python 2.5-
A.print_x.im_func(b)
a.print_x.im_func(b)
# python 2.6+
A.print_x.__func__(b)
a.print_x.__func__(b)
In python 3 there's no such thing anymore as unbound method.
Unbound methods are gone for good. ClassObject.method returns an
ordinary function object, instance.method still returns a bound
method object. >> http://www.python.org/getit/releases/3.0/NEWS.txt
Hence, in python 3, A.print_x is just a function, and can be called right away and a.print_x still has to be unbounded:
# python 3.0+
A.print_x(b)
a.print_x.__func__(b)
You don't (well, it's not that you can't throw enough magic at it to make it work, it's that you just shouldn't). If the function is supposed to work with more than one type, make it... a function.
# behold, the magic and power of duck typing
def fun(obj):
print obj.x
class A:
x = 42
class B:
x = 69
fun(A())
fun(B())
I don't know why you would really want to do this, but it is possible:
>>> class A(object):
... def foo(self):
... print self.a
...
>>> class B(object):
... def __init__(self):
... self.a = "b"
...
>>> x = A()
>>> y = B()
>>> x.foo.im_func(y)
b
>>> A.foo.im_func(y)
b
An instance method (a class instance's bound method) has a property called im_func which refers to the actual function called by the instance method, without the instance/class binding. The class object's version of the method also has this property.

Categories

Resources