Overriding method with similar body - python

I have two classes: One and Two
class One:
# self.a, self.b, self.c
# ...
def foo(self):
self.a.foo()
self.b.bar()
self.c.hmm(1,2,3)
class Two(One):
# super(Two, self).__init__()
# self.d
# ...
def foo(self):
self.a.foo()
self.b.bar()
self.d.wow()
self.c.hmm(4,5,6)
One and Two's foo() methods are similar enough that I feel like I'm copy-pasting code.
I know I could have a separate foo2() method in One that executes the shared code and add arguments to foo() for the different values, but I'm wondering if there's a better way to do this.

To extend a method from a super class, you can use super.
class One:
...
def foo(self):
self.a.foo()
self.b.bar()
self.c.hmm(1,2,3)
class Two(One):
...
def foo(self):
super().foo()
self.d.wow()
Notice this will not preserve the order in which the methods are called. So if that order matters you do have to rewrite the whole foo method.

Related

How to create new subclasses by adding the same method to multiple classes

I have 5 classes that are all subclasses of a parent class. I want to define new subclasses for each of these classes by adding an identical method to each of them. Is there a way to do this dynamically? Does this have a name in OOP terminology?
In below new_method is exactly the same for all cases so I'd prefer to repeat myself.
class A(MySuperClass)
class B(MySuperClass)
class C(MySuperClass)
class D(MySuperClass)
class AAA(A):
def new_method():
...
class BBB(B):
def new_method():
...
class CCC(C):
def new_method():
...
class DDD(D):
def new_method():
...
You can solve repetition by creating new class that contains your new_method as follows:
class A(MySuperClass)
class B(MySuperClass)
class C(MySuperClass)
class D(MySuperClass)
class Mixin():
def new_method():
pass
class AAA(A, Mixin):
pass
This is called multi-inheritance. you can consider here inheritance as both specialization mechanism and also code sharing.
Besides multiple inheritance, you can also use a decorator if it is more convenient to your code:
def add_new_method(cls):
def new_method(self, ...):
pass
cls.new_method = new_method
return cls
#add_new_method
class AAA(A):
...
However, if you don't necessarily need a new AAA subclass, but just wanted to add new_method to A, B, C, D from the superclass, then it's as easy as:
def new_method(self, ...):
pass
A.new_method = new_method
Even better yet. If MySuperClass can be changed, you can simply do:
MySuperClass.new_method = new_method
# A, B, C and D will all have new_method automatically.
You can use type to dynamically create classes:
class Foo(object):
def __init__(self):
print("NOTHING")
def func(self):
print("ARRRGH")
Bar = type('Bar', (Foo,), {"func": func})
Bar2 = type('Bar2', (Foo,), {"func": func})
Bar3 = ... # Put in loop if you like.
b = Bar() # This will print "NOTHING", Bar2, Bar3 works the same way.
b.func() # will print "ARRRGH"

Identify the superclass that defines a class-level variable

In the case of multiple inheritance in python, is there a way to identify which super class a class-level variable is obtained from?
All attempts I tried to google are overwhelmingly about How to get the attribute not find out where it came from:
https://www.google.com/search?q=pythin+which+super+class+defines+attr
https://www.google.com/search?q=python+which+super+class+has+attribute&oq=python+which+super+class+has+attr
https://www.google.com/search?q=python+which+super+class+attribute+obtained+from
I suppose I can manually step through the MRO using inspect.getmro(cls). But I couldn't find any more elegant solutions. Just wondering if anyone knows of one.
EDIT
For a concrete example:
class Super1(object):
__class_attribute__ = "Foo"
class Super2(object):
pass
class Derived(Super1, Super2):
pass
d = Derived()
parent_cls = some_function_to_get_defining_class(d.__class_attribute__) # <-- should return `Super1`
The __qualname__ attribute gives an indication from which class a method was inherited. However, this only returns a string, not the superclass itself. If you need to the superclass for metaprogramming, I think you are going to have to dig into the MRO.
class A:
def a(self):
return 1
def b(self):
return 2
class B:
def b(self):
return 2.5
def c(self):
return 3
class C(A,B):
pass
Using:
C.b.__qualname__
# returns:
'A.b'
However, this does not apply when using abstract methods to define an interface, since the method has to be overwritten.
from abc import abstractmethod
class A:
def a(self):
return 1
#abstractmethod
def b(self):
pass
class C(A):
def b(self):
return 100
C.b.__qualname__
# returns:
'C.b'

Python multiple inheritance constructor not called when using super()

Consider the following code:
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
super().__init__()
print(self.get_something())
and then do:
c = C()
which results in something like this:
AttributeError: 'C' object has no attribute 'something'
I suppose this happens due to the constructor of B not being called when using super(). Is there a way to achieve the correct behavior with Python 3?
Superclasses should use super if their subclasses do. If you add the super().__init__() line into A and B your example should work again.
Check the method resolution order of C:
>>> C.mro()
[__main__.C, __main__.A, __main__.B, builtins.object]
This article should clear things up.
As others have mentioned, the method resolution order is key here. If you want to call multiple superclass constructors, then you will have to call them directly.
class A(object):
def __init__(self):
pass
class B(object):
def __init__(self):
self.something = 'blue'
def get_something(self):
return self.something
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print(self.get_something())

Python recursive method calls with super

I am working with a library that relies on a recursive method call:
class A(object):
def __init__(self):
self.foo = None
def f(self):
if not self.foo:
print("Hello")
self.foo = 100
self.f()
I would like to override the method f() while using the original implementation:
class B(A):
def f(self):
super(B, self).f()
print("World")
This way, I hope to get:
Hello
World
Instead, I see:
Hello
World
World
I understand this is because the original code in class A calls self.f(), which finds B.self.
Question: What is the most Pythonic way to have "super(B, self).f()" treat self as class A, call A.f() recursively, and then return to B.f() to print "World?"
Thanks.
The only way I can see this work is for A.f() to not use self.f() but to use A.f(self) instead.
A better design is for A.f() to delegate the recursive call to a separate method:
class A(object):
def __init__(self):
self.foo = None
def f(self):
self._f_recursive()
def _f_recursive(self):
if not self.foo:
print("Hello")
self.foo = 100
self._f_recursive()
If your only option lies in B, then apart from don't override f() then, is to lie about the class, temporarily. This is not Pythonic or recommended but it'll work:
class B(A):
def f(self):
try:
self.__class__, cls = A, self.__class__
A.f(self)
finally:
self.__class__ = cls
print("World")
To be clear about this: this is not thread-safe nor the proper way to deal with this.

name of the class that contains the method code

I'm trying to find the name of the class that contains method code.
In the example underneath I use self.__class__.__name__, but of course this returns the name of the class of which self is an instance and not class that contains the test() method code. b.test() will print 'B' while I would like to get 'A'.
I looked into the inspect module documentation but did not find anything directly useful.
class A:
def __init__(self):
pass
def test(self):
print self.__class__.__name__
class B(A):
def __init__(self):
A.__init__(self)
a = A()
b = B()
a.test()
b.test()
In Python 3.x, you can simply use __class__.__name__. The __class__ name is mildly magic, and not the same thing as the __class__ attribute of self.
In Python 2.x, there is no good way to get at that information. You can use stack inspection to get the code object, then walk the class hierarchy looking for the right method, but it's slow and tedious and will probably break when you don't want it to. You can also use a metaclass or a class decorator to post-process the class in some way, but both of those are rather intrusive approaches. And you can do something really ugly, like accessing self.__nonexistant_attribute, catching the AttributeError and extracting the class name from the mangled name. None of those approaches are really worth it if you just want to avoid typing the name twice; at least forgetting to update the name can be made a little more obvious by doing something like:
class C:
...
def report_name(self):
print C.__name__
inspect.getmro gives you a tuple of the classes where the method might come from, in order. As soon as you find one of them that has the method's name in its dict, you're done:
for c in inspect.getmro(self.__class__):
if 'test' in vars(c): break
return c.__name__
Use __dict__ of class object itself:
class A(object):
def foo(self):
pass
class B(A):
pass
def find_decl_class(cls, method):
if method in cls.__dict__:
return cls
for b in cls.__bases__:
decl = find_decl_class(b, method)
if decl:
return decl
print 'foo' in A.__dict__
print 'foo' in B.__dict__
print find_decl_class(B, 'foo').__name__
Will print True, False, A
You can use (abuse?) private name mangling to accomplish this effect. If you look up an attribute on self that starts with __ from inside a method, python changes the name from __attribute to _classThisMethodWasDefinedIn__attribute.
Just somehow stash the classname you want in mangled-form where the method can see it. As an example, we can define a __new__ method on the base class that does it:
def mangle(cls, attrname):
if not attrname.startswith('__'):
raise ValueError('attrname must start with __')
return '_%s%s' % (cls.__name__, attrname)
class A(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls)
for c in cls.mro():
setattr(obj, mangle(c, '__defn_classname'), c.__name__)
return obj
def __init__(self):
pass
def test(self):
print self.__defn_classname
class B(A):
def __init__(self):
A.__init__(self)
a = A()
b = B()
a.test()
b.test()
which prints:
A
A
You can do
>>> class A(object):
... def __init__(self):
... pass
... def test(self):
... for b in self.__class__.__bases__:
... if hasattr(b, 'test'):
... return b.__name__
... return self.__class__.__name__
...
>>> class B(A):
... def __init__(self):
... A.__init__(self)
...
>>> B().test()
'A'
>>> A().test()
'A'
>>>
Keep in mind that you could simplify it by using __class__.__base__, but if you use multiple inheritance, this version will work better.
It simply checks first on its baseclasses for test. It's not the prettiest, but it works.

Categories