Taking Python class method as a polymorphic function - python

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

Related

Replace method with method of instance of different class

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

How can I make a class which has a public method that cannot be inherited by its sub-class?

I want to make a class A like this
class A:
#someDecorator
def f(self):
print('A.f')
def g(self):
print('A.g')
And I can use it like this
a = A()
a.f() #prints A.f
a.g() #prints A.g
But if I declare a class B as a subclass of A, attribute f of class B will become invisible
class B(A):
pass
b = B()
b.g() #prints A.g
b.f() #error: no such attribute
How can I make this #someDecorator?
One way is to check __class__ of the method, and return if it matches A.
def disable_method(func):
def func_wrapper(name):
if name.__class__ == A:
return func(name)
else:
raise AttributeError("Cannot call inherited method")
return func_wrapper
class A:
#disable_method
def f(self):
print('A.f')
def g(self):
print('A.g')
class B(A):
pass
a = A()
a.f()
#A.f
b = B()
b.f()
#AttributeError: Cannot call inherited method
Edit: Looking at this again it is possible to check whether the method's class has a superclass by __bases__. It should work as long as its being used as a decorator.
def disable_method(func):
def func_wrapper(name):
if name.__class__.__bases__ == object:
return func(name)
else:
raise AttributeError("Cannot call method from superclass")
return func_wrapper

Dynamically adding class instance to Python instance à la __setattr__

Suppose I have two Python classes, A and B. I would like to be able to do the following:
>>> b = B()
>>> b.a.attr1 = 'foo'
>>> b.a.attr2 = 'bar'
where 'a' is an instance of A. I can't use __setattr__ as I would if 'a' was some
"primitive" type. Is there some elegant way to accomplish this, other than
>>> b = B()
>>> b.a = A()
>>> b.a.attr1 = 'foo'
>>> b.a.attr2 = 'bar'
?
You'd have to either create a in the __init__ of B, use a __getattr__ hook to create a dynamically, or use a property.
__init__ approach:
class B(object):
def __init__(self):
self.a = A()
__getattr__ approach:
class B(object):
def __getattr__(self, attr):
if attr == 'a':
self.a = A()
return self.a
raise AttributeError(attr)
Property approach:
class B(object):
_a = None
#property
def a(self):
if self._a is None:
self._a = A()
return self._a
Of course, the property and __getattr__ approaches do not have to store the A() instance on self, it could just return a pre-existing A() instance from somewhere else instead.
class A(object):
pass
class B(object):
def __init__(self):
self.a = A()
b = B()
b.a.attr1 = 'foo'
b.a.attr2 = 'bar'

How to make python function always unbound even assigned to a class attribute

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.

How can I tell what class in the polymorphic hierarchy contains the method I'm about to call?

If I have:
class A():
def f(self):
print("running function, f from class A")
class B(A):
def __init__(self):
A.__init__(self)
def f(self):
print("running function, f from class B")
and I make an instance of class B and call f on it, we all know we'll see the message about "from class B." But is there a way for me to inspect my object and make sure my sub-class has overridden my method? Something like:
obj = B()
assert(not obj.f.livesIn(A))
class A():
def f(self):
print("running function, f from class A")
class B(A):
def f(self):
print("running function, f from class B")
class C(A):
pass
This shows that B.f does not equal A.f. So B must override f:
obj = B()
print(obj.__class__.f == A.f)
# False
This shows that C.f equals A.f. So C must not have overridden f:
obj = C()
print(obj.__class__.f == A.f)
# True
If you want to force the child class to override, you can raise NotImplementedError().
Doing the inspection is possible too... And I see unutbu just posted an example, so I won't repeat it. :)

Categories

Resources