I just tried to make a system with some classes with similar parent class, which dynamically assigns their method between them, It looks like one function "transform(from, to)", which doing smth like from.foo = to.foo
So, you see, that my problem is difficult with Python, so I'm asking for help
My first try was
def transform(a, b):
a.foo = b.foo
class A:
def foo(self):
pass
class B(A):
def foo(self):
print("bar")
transform(self, C())
class C(A):
def foo(self):
print("foo")
transform(self, B())
b = C()
b.foo()
b.foo()
b.foo()
b.foo()
I want to get from it smth like
foo
bar
foo
bar
But I get
foo
bar
bar
bar
The funniest moment of all is that first conversion working, but next.
My next try was
class A:
def foo(self):
pass
def transform(self, to):
self.foo = to.foo
class B(A):
def foo(self):
print("bar")
self.transform(C())
class C(A):
def foo(self):
print("foo")
self.transform(B())
b = C()
b.foo()
b.foo()
b.foo()
b.foo()
and I got the same result. Do you have any idea how to manage this conversion?
In your code, it first runs a.foo = b.foo, where a was the object created at b = C(), and b was the object created at B(). Then when a.foo() is next run, it runs B's foo(self) where self is that B() object. There, it runs transform(self, C()) where self is still the object created with B(), not the object at b. Basically, b is only modified once then never modified again.
Instead, you should get the unbound function object of foo with b.foo.__func__ before assigning it to the other one, or the self in foo will refer to the object B() instead of b. Then, you can bind it back to the destination object with types.MethodType:
from types import MethodType
def transform(a, b):
a.foo = MethodType(b.foo.__func__, a)
class A:
def foo(self):
pass
class B(A):
def foo(self):
print("bar")
transform(self, C())
class C(A):
def foo(self):
print("foo")
transform(self, B())
b = C()
b.foo()
b.foo()
b.foo()
b.foo()
foo
bar
foo
bar
Your 2nd attempt is also fixed by doing the same thing:
from types import MethodType
class A:
def foo(self):
pass
def transform(self, to):
self.foo = MethodType(to.foo.__func__, self)
class B(A):
def foo(self):
print("bar")
self.transform(C())
class C(A):
def foo(self):
print("foo")
self.transform(B())
b = C()
b.foo()
b.foo()
b.foo()
b.foo()
foo
bar
foo
bar
Related
Edit: this question is based on two mistakes: not originally yincluding self in methods and assuming unbounded methods would not exhibit polymorphism, but they do. I voted to close it.
I can take a method as a function-valued object in Python:
class A:
def foo(self):
return "Hi, I'm A.foo"
f = A.foo
a = A()
print(f(a))
produces Hi, I'm A.foo.
So far so good. However, f won't work for sub-classes:
class B(A):
def foo(self):
return "Hi, I'm B.foo"
f = A.foo
b = B()
f(b)
still produces Hi, I'm A.foo, whereas b.foo() produces Hi, I'm B.foo. In other words, polymorphism does not apply.
Question: is there a way to get a function-valued object in f so that f(x) == x.foo() whenever isinstance(x, A)?
In other words, how do I complete the snippet below for things to work?
class A:
def foo(self):
return "Hi, I'm A.foo"
class B(A):
def foo(self):
return "Hi, I'm B.foo"
f = <WHAT DO I PUT HERE?>
a = A()
b = B()
assert f(a) == a.foo()
assert f(b) == b.foo()
Your can pull the class name out like so:
class A:
def foo(self):
return "Hi, I'm {}.foo".format(self.__class__.__name__)
class B(A):
def foo(self):
return "Hi, I'm {}.foo".format(self.__class__.__name__)
f = A.foo # this could be A or B, doesn't matter
a = A()
b = B()
assert f(a) == a.foo()
assert f(b) == b.foo()
Note that because B inherits from A, you don't even need the foo method there:
class A:
def foo(self):
return "Hi, I'm {}.foo".format(self.__class__.__name__)
class B(A):
pass
f = A.foo
a = A()
b = B()
assert f(a) == a.foo()
assert f(b) == b.foo()
I have the following simplified class structure
class A():
def foo(self):
self.bar()
def bar(self):
print("A bar")
class B(A):
def bar(self):
print("B bar")
class C(A):
def bar(self):
print("C bar")
Now I also have a modified version of A
class ModA():
def foo(self):
print("hi")
self.bar()
I now also want a additional versions of B and C that have ModA as parent class instead of A.
class ModB(ModA):
def bar(self):
print("B bar")
class ModC(ModA):
def bar(self):
print("C bar")
How do I define ModB and ModC without duplicating their entire definition except for the first line that defines the parents?
It is impossible to answer without knowing what your "slight changes" might be, but in general, look at multi-inheritance - class ModB(ModA, B): might be what you need.
class ModB(ModA, B):
pass
>>> b = ModB()
>>> b.foo()
hi
B bar
Based on your toy example here is one way to do it.
class FooBase:
def foo(self):
self.bar()
class FooMod:
def foo(self):
print("hi")
self.bar()
class A(FooBase):
def bar(self):
print("A bar")
class B(FooBase):
def bar(self):
print("B bar")
class C(FooBase):
def bar(self):
print("C bar")
class ModA(FooMod,A):
pass
class ModB(FooMod,B):
pass
class ModC(FooMod,C):
pass
class BadModA(A,FooMod):
pass
for thing in [A,B,C,ModA,ModB,ModC,BadModA]:
thing = thing()
print(thing.foo())
print('**********')
You have to be careful how you order the base classes notice BadModA doesn't work because it finds the foo method in FooBase before looking in FooMod. You can see that in their Method Resolution Order's
In [15]: ModA.__mro__
Out[15]: [__main__.ModA, __main__.FooMod, __main__.A, __main__.FooBase, object]
In [16]: BadModA.__mro__
Out[16]: (__main__.BadModA, __main__.A, __main__.FooBase, __main__.FooMod, object)
While this works it seems like you could get yourself in trouble. you always have to remember which order to use - maybe someone will comment.
Here is another way, it flattens the structure out a bit, five bases and you mix them up
class FooBase:
def foo(self):
self.bar()
class FooMod:
def foo(self):
print("hi")
self.bar()
class Abar:
def bar(self):
print("A bar")
class Bbar:
def bar(self):
print("B bar")
class Cbar():
def bar(self):
print("C bar")
class A(FooBase,Abar):
pass
class B(FooBase,Bbar):
pass
class C(FooBase,Cbar):
pass
class ModA(FooMod,Abar):
pass
class ModB(FooMod,Bbar):
pass
class ModC(FooMod,Cbar):
pass
When I tried to call the bar() of class B from class C, which is a direct subclass of B, it turned out that the bar() of class A was called. But I explicitly required that the B version should be used. How can the method be resolved to that of A?
class A(object):
def bar(self):
print('bar from A')
class B(A):
def bar(self):
print('bar from B')
class C(B):
def bar(self):
super(B, self).bar()
c = C()
# It should print "bar from B"
c.bar()
# But actually it prints "bar from A"
It's because super(sub_class, instance).method() means call the method method of the parent of sub_class on the instance instance. Since the parent of B is A, the result makes sense.
What you want is super(C, self).bar().
I am wondering if there is a way to programatically write an attribute to the class of a parent in which a function is run. I am looking for something like the following.
def add_items():
setattr(ParentClass, 'foo', 'bar')
class A(object):
add_items()
class B(object):
add_items()
a = A()
b = B()
print a.foo
"bar"
print b.foo
"bar"
It is not clear to me what you are after. Here are two attempts to answer your question.
You can make a decorator which adds attributes to a class:
def add_items(cls):
cls.foo = "bar"
return cls
#add_items
class A(object):
pass
On the other hand, if your function is a method, it can add whatever attributes it wants to the class instance:
class B(object):
def add_items(self):
self.foo = 'Baz'
b = B()
b.add_items()
Having the following code, what can I do with the 'foo' function to make it unbound in any case even if it is assigned to class attribute? Overriding __get__ doesn't help - as far as I understand because it is not used when function is absent from __dict__ of an instance (it is so in case of class attributes).
But what else can be done here?
def foo(x):
print(x)
def foo_get(self, obj, type=None):
return foo
foo.__get__ = foo_get
class A(object):
def __init__(self):
self.f = foo
class B(object):
f = foo
a = A()
print(a.f) #<function foo at 0x2321d10>
print(a.f.__get__(a, A)) #<function foo at 0x2321d10>
b = B()
print(b.f) #<bound method B.foo of <__main__.B object at 0x23224d0>>
I'm pretty sure you want #staticmethod.
foo = staticmethod(foo)
You can also define __get__ on a callable class:
class Foo(object):
def __get__(self, obj, type=None):
return self
def __call__(self, x):
print(x)
foo = Foo()
class A(object):
def __init__(self):
self.f = foo
class B(object):
f = foo
foo(1) # 1
a = A()
a.f(1) # 1
print(a.f) #<function foo at 0x2321d10>
print(a.f.__get__(a, A)) #<function foo at 0x2321d10>
b = B()
b.f(1) # 1
print(b.f) #<__main__.Foo object at 0x7fa8c260be10>
It's one extra level of indentation and a few extra lines of code, but that's how it can be done, if it's important.