How to change Method of Object in python - python

I write a python file like :
class A(object):
def update(self, str):
pass
def say(self, str):
print "I update: " + str
def fun(obj, str):
obj.say(str)
a = A()
import types
setattr(A, "update", types.MethodType(fun, None, A))
a.update("hello")
b = A()
b.update("world?")
It change behave of class, the object b have been changed. but, I want to only change object a.
How to change Method of Object in python?

Here is a way to do it:
a.update = lambda x: fun(a, x)

You are setting the class method, while you want to set only the method bound to some instance.
>>> class MyClass(object):
... def a(self): pass
...
>>> MyClass.a = lambda x: x
>>> MyClass.a
<unbound method MyClass.<lambda>>
>>> a = MyClass()
>>> a.a
<bound method MyClass.<lambda> of <__main__.MyClass object at 0x1d7fed0>>
Changing the a method at class level changes also the a methods of all instances.
>>> class MyClass(object):
... def a(self): pass
...
>>> b = MyClass()
>>> b.a = lambda x: x
>>> MyClass.a
<unbound method MyClass.a>
>>> b.a
<function <lambda> at 0x1d88938>
>>> c = MyClass()
>>> c.a
<bound method MyClass.a of <__main__.MyClass object at 0x1d8d110>>
Changing the a method of an instance does not change the method of the class or other instances.

Related

Can we use super() to test identity between class methods in the MRO?

Consider the following example:
class A:
def m():
pass
class B(A):
pass
And the following terminal output:
>>> b = B()
>>> b.m
<bound method A.m of <__main__.B object at 0x000001EFFF24C748>>
>>> super(b.__class__, b).m
<bound method A.m of <__main__.B object at 0x000001EFFF24C748>>
>>> b.m is super(b.__class__, b).m
False
>>> b.m == super(b.__class__, b).m
True
Why are they equal but not identical? Is a copy of the method made when it is inherited?
Are there better ways to test whether a child class has overridden a parent method?
You can use the __dict__ attribute to check which methods and attributes have been overridden:
>>> class A:
... def m():
... pass
...
>>> class B(A):
... pass
...
>>> class C(A):
... def m():
... pass
...
>>> 'm' in A.__dict__
True
>>> 'm' in B.__dict__
False # not overridden
>>> 'm' in C.__dict__
True # overridden
Using super(b.__class__, b) produces an object that implements a __getattr__ method that will go up the __mro__ attribute starting at position 1 (skipping the current class) and look for the first class that has the specified attribute. It will then return that bound method. For a better explanation see this answer.
Knowing that all functions are also descriptors the following
class A:
def m(self):
pass
creates an object A with the attribute m which will be a function and descriptor. When you initialize an object a of class A, it will basically result in a.m = A.m.__get__(a) which produces the bound method that has a as the first argument self.
Now since super also retrieves bound methods what is being checked is the identity between 2 instances of A.m.__get__(a) producing your terminal output:
>>> A.m.__get__(a)
<bound method A.m of <__main__.A object at 0x...>>
>>> A.m.__get__(a) is A.m.__get__(a)
False
So 2 calls to the class descriptor m produce different bound instances and it is why the identity check fails. Instead you should test the identity of the functions that produced the bound methods. Luckily a bound method contains the __func__ attribute that returns the original function. So to lookup whether any instance's class has overridden an inherited function without knowing more than just the instance and name of the function you can do:
>>> a.__class__.m is super(a.__class__, a).m.__func__
True

instantiating two classes with references to each other

I have two classes, B and C.
I want to instantiate B with C reference and C with B reference.
I could add a setter method, but was wondering if I can do it in the __init__ stage or any other elegant ways
It is not possible within __init__ directly due to a chicken and egg situation. However, it is possible in one assignment statement:
>>> class A:
... pass
...
>>> class B:
... pass
...
>>> a, b = b.a, a.b = A(), B()
>>> a.b is b
True
>>> b.a is a
True
This relies on the fact that Python evaluates assignments left to right.
It is not thread safe; if you need to guarantee that the references exist in a threaded application then you'll want to use a mutex to handle the possible race conditions. The GIL works at the opcode level, which is a finer-grained resolution than lines of Python code.
You could do it in __init__ if you make one of the class initializers take an object of the other class:
>>> class B:
... def __init__(self):
... self.c = C(self)
...
>>> class C:
... def __init__(self, b):
... self.b = b
...
>>> b = B()
>>> c = b.c
>>> b.c
<__main__.C object at 0x107a4f6d8>
>>> b.c.b.c
<__main__.C object at 0x107a4f6d8>
>>> b.c.b.c.b
<__main__.B object at 0x107a60e80>
>>> b
<__main__.B object at 0x107a60e80>
>>> c
<__main__.C object at 0x107a4f6d8>
>>> c.b
<__main__.B object at 0x107a60e80>
>>> b.c
<__main__.C object at 0x107a4f6d8>
>>> b.c.b.c
<__main__.C object at 0x107a4f6d8>
>>> c.b.c.b
<__main__.B object at 0x107a60e80>
Or even without any arguments to __init__:
>>> class B:
... def __init__(self):
... self.c = C()
... self.c.b = self
...
>>> class C:
... pass
...
>>> b = B()
>>> c = b.c
>>> b
<__main__.B object at 0x10835c048>
>>> c
<__main__.C object at 0x1085ccac8>
>>> b.c
<__main__.C object at 0x1085ccac8>
>>> c.b
<__main__.B object at 0x10835c048>
>>> b.c.b.c.b
<__main__.B object at 0x10835c048>

Get name of the bound method from instance of the bound method object in Python

I have :
class A:
def a():
pass
After typing in the python command line:
Aobj = A()
aBoundMeth = getattr(Aobj, 'a')
My goal is to get the name of the method that aBoundMeth object represents. Is it possible to do it?
Thank you in advance.
Assuming that the name of the method is the string 'a' (in this case), You can use the __name__ attribute on the function object.
e.g.
>>> Aobj = A()
>>> aBoundMeth = getattr(Aobj, 'a')
>>> aBoundMeth.__name__
'a'
Note that this is the function name when it was created. You can make more references to the same function, but the name doesn't change. e.g.
>>> class A(object):
... def a(self):
... pass
... b = a
...
>>> Aobj = A()
>>> Aobj.a.__name__
'a'
>>> Aobj.b.__name__
'a'

Python: Why do an unbound method with two params behave other than a class method?

In this piece of code:
def fa(a,b):
print a
print b
print " fa called"
class A:
#classmethod
def fa(a,b):
print a
print b
print " A.fa called"
class B:
def __init__(s,a):
s.a = a
obj1 = B(A.fa)
obj1.a("xxxx")
obj2 = B(fa)
obj2.a("xxxx")
Output:
__main__.A
xxxx
A.fa called
Traceback (most recent call last):
File "test.py", line 20, in <module>
obj2.a("xxxx")
TypeError: fa() takes exactly 2 arguments (1 given)
Why is the free method "fa" not receiving the "self" as a first parameter? The bound method A.fa behaves as expected.
The bound method A.fa receives A as the first parameter because it is a class method of A. No matter how you call this function it will always receive A as the first parameter.
The free method fa is not bound, so the only arguments it will receive are the ones that are passed in. No matter how you call this function, it will never receive parameters other than the ones that are passed in.
This behavior is different from a language like JavaScript, where how the method is called determines the context. In Python the implicit argument passing (similar to JavaScript context) is determined at the function definition time, and that binding or lack thereof will always be used for that function regardless of how it is called.
If you want to dynamically bind a free method you can do this using types.MethodType, for example:
def fa(x):
print x
class B: pass
>>> obj1 = B()
>>> obj1.a = MethodType(fa, obj1)
>>> obj1.a() # obj1.a behaves like an instance method bound to obj1
<__main__.B instance at 0x7f0589baf170>
>>> obj1.a2 = MethodType(fa, B)
>>> obj1.a2() # obj1.a2 acts like a class method bound to B
__main__.B
Because doing obj2.a = fa does not make a (fa) a method of obj2:
>>> class A(object):
... def meth(self, x, y):
... print x, y
...
>>>
>>> a = A()
>>>
>>> a.meth
<bound method A.meth of <__main__.A object at 0x10e281950>> # Method
>>>
>>> def fn(x, y):
... print x, y
...
>>>
>>> fn
<function fn at 0x10e287140>
>>> a.fn = fn
>>>
>>> a.fn
<function fn at 0x10e287140> # Not a method, still a function

How do I assign a property to an instance in Python?

Using python, one can set an attribute of a instance via either of the two methods below:
>>> class Foo(object):
pass
>>> a = Foo()
>>> a.x = 1
>>> a.x
1
>>> setattr(a, 'b', 2)
>>> a.b
2
One can also assign properties via the property decorator.
>>> class Bar(object):
#property
def x(self):
return 0
>>> a = Bar()
>>> a.x
0
My question is, how can I assign a property to an instance?
My intuition was to try something like this...
>>> class Doo(object):
pass
>>> a = Doo()
>>> def k():
return 0
>>> a.m = property(k)
>>> a.m
<property object at 0x0380F540>
... but, I get this weird property object. Similar experimentation yielded similar results. My guess is that properties are more closely related to classes than instances in some respect, but I don't know the inner workings well enough to understand what's going on here.
It is possible to dynamically add properties to a class after it's already created:
class Bar(object):
def x(self):
return 0
setattr(Bar, 'x', property(Bar.x))
print Bar.x
# <property object at 0x04D37270>
print Bar().x
# 0
However, you can't set a property on an instance, only on a class. You can use an instance to do it:
class Bar(object):
def x(self):
return 0
bar = Bar()
setattr(bar.__class__, 'x', property(bar.__class__.x))
print Bar.x
# <property object at 0x04D306F0>
print bar.x
# 0
See How to add property to a class dynamically? for more information.
Properties use descriptors which only work on classes and thus
for all instances. But you could use a combination of a descriptor on
a class that would consult a per-instance function.
>>> class Foo(object):
... #property
... def x(self):
... if 'x' in self.__dict__:
... return self.__dict__['x'](self)
...
>>> a = Foo()
>>> def k(self):
... return 0
...
>>> a.__dict__['x'] = k
>>> a.x
0
You can assign the property directly to the class object:
>>> class Foo(object):
pass
>>> a = Foo()
>>> a.__class__
__main__.Foo
>>> setattr(a.__class__, 'm', property(lambda self: 0))
>>> a.m
0
>>> a.m = 24
AttributeError: can't set attribute
Here we have taken #agf's solution and used a lambda function to define the class property.
class A(object):
pass
a = A()
a.__class__.f = property(lambda self: 57)
a.f # 57
The following post provides more context: https://crosscompute.com/n/jAbsB6OIm6oCCJX9PBIbY5FECFKCClyV/_/Assign%20a%20class%20property%20to%20an%20instance

Categories