Calling method of another parent of the same class - python

Is it possible for a class to call a method of another class through super() if they are both parents of the same class?
class A:
def a(self, x):
return x * 2
class B:
def b(self, x):
tmp = super().a(x) # <---
return tmp + 2
class C(A, B):
def c(self):
return super().b(20)
print(C().c())
Result:
AttributeError: 'super' object has no attribute 'a'
I thought this would work because C.mro() is [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] (so A is in it) and it's the callee's MRO that's supposed to be defining.

Yes, but not quite like this.
A class's MRO is a total order over all ancestors of a class, including ancestors with no superclass relation between them. A and B aren't treated equally; A comes first in C's MRO (required because A was listed as a base class before B in the class statement), so the super lookup will start after B in C's MRO and skip past A entirely.
For your b method's super lookup to work, C would have had to be created as class C(B, A): .... Depending on your goal, you might instead want to just replace all those super().whatever calls with self.whatever, if you don't actually want the override-skipping behavior of super. If you do want override-skipping, but you want it in a way where A and B are treated symmetrically, you may have to build your own mechanism for that.

I believe you can just use tmp = self.a(x) and it should work as expected. Using super() is for accessing base classes. Here you just need an instance variable.

It's difficult to imagine a situation where this is genuinely something you would want to do. Such a method can only be called on objects which are instances of both A and B. #user2357112 has given the strictly correct answer to this question, but as pointed out in a comment, the solution using super() is not good design. So I will give an answer in case this isn't an abstract language-lawyer puzzler, and you (or others with the same question) are writing real code.
If C is the only class which will extend both A and B, then the simplest thing you can do is write the b method in class C.
Otherwise, if multiple classes might extend both A and B, then you can declare a "base" class for all such extensions, put the b method there, and make other classes inherit from that instead of A and B separately:
class A:
def a(self, x):
return x * 2
class B:
pass
class A_and_B(A, B):
def b(self, x):
tmp = self.a(x)
return tmp + 2
class C(A_and_B):
def c(self):
return self.b(20)
This way, instances of B which are not instances of A won't have the b method, which is desirable because the method can't be called on non-instances of A anyway.
The code above calls whichever implementation of a the object has, which could be overridden by a subclass. If you absolutely must call the A class's implementation of a, not some overridden version of it, then you can invoke A.a directly:
tmp = A.a(self, x)
This is still bad design, but not quite as bad as hacking super() to call a method on a class which is not actually a superclass.

Related

How do I call an indirect parent class's method from a child class in Python? [duplicate]

This question already has an answer here:
How to call method of second parent class using method of child class in python?
(1 answer)
Closed 10 months ago.
I have classes A, B, and C. C inherits both from A and B, and for the most part wherever there are methods overriding each other I want A's to override B's, so I have the following structure:
class A:
def some_method(self):
return self.some_attribute
class B:
def some_method(self):
return self.some_other_attribute
class C(A,B):
def some_method(self):
var = super().some_method() -> this will give me the method from A, but I want the B one
return var + 1
So my question is how can I invoke with super() or some other way the some_method() from B, so I can use it in the method override in C?
Also for the record I am using Python 3.*
Python is a language with multiple inheritance, which means that a class can have several direct superclasses. But for the purposes of calling super(), Python always linearizes the inheritance hierarchy to determine who the "best" superclass is. This is called the method resolution order, accessed via the __mro__ variable in Python.
So if we have the class hierarchy
class A(object): pass
class B(A): pass
class C(A): pass
class D(B, C): pass
Then although D has two superclassses, if we say super() in D, it'll only give us B. That's because the MRO for D creates a linear hierarchy for us. In Python 3, it uses an algorithm called the C3 resolution, which gives us this hierarchy
>>> D.__mro__
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
So if we call super() in a method on class D, we'll get the version on B, and if we call super() from there, we'll get the C version, then the A version, and finally the object one.
So in your case, C inherits from A and B (in that order), so it's going to be biased toward A. That is, the MRO for the class C in your example is (C, A, B, object). If you want it to always go for B and then A, then simply switch the order of the superclasses.
class C(B, A):
...
This is the best solution. It's predictable and Python programmers will immediately understand the intention of a super() call in this class. I recommend this if it fits your needs.
However, if you have a complicated multi-inheritance system and need access to both the A and B superclasses at the same time, you can always call them directly with a class name, forgoing the super() call altogether.
class C(B, A):
def some_method(self):
A.some_method(self) # Calls A's version on self
B.some_method(self) # Calls B's version on self
If some_method takes some arguments, you can pass them after the explicit self argument. This is going to raise more eyebrows, so if you find yourself needing access to two distinct superclass methods in complicated ways, you might consider refactoring your code to use less multiple inheritance. But the feature is there if you really need it.

Mix-in of abstract class and namedtuple

I want to define a mix-in of a namedtuple and a base class with defines and abstract method:
import abc
import collections
class A(object):
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def do(self):
print("U Can't Touch This")
B = collections.namedtuple('B', 'x, y')
class C(B, A):
pass
c = C(x=3, y=4)
print(c)
c.do()
From what I understand reading the docs and other examples I have seen, c.do() should raise an error, as class C does not implement do(). However, when I run it... it works:
B(x=3, y=4)
U Can't Touch This
I must be overlooking something.
When you take a look at the method resolution order of C you see that B comes before A in that list. That means when you instantiate C the __new__ method of B will be called first.
This is the implementation of namedtuple.__new__
def __new__(_cls, {arg_list}):
'Create new instance of {typename}({arg_list})'
return _tuple.__new__(_cls, ({arg_list}))
You can see that it does not support cooperative inheritance, because it breaks the chain and simply calls tuples __new__ method. Like this the ABCMeta.__new__ method that checks for abstract methods is never executed (where ever that is) and it can't check for abstract methods. So the instantiation does not fail.
I thought inverting the MRO would solve that problem, but it strangely did not. I'm gonna investigate a bit more and update this answer.

Should I use super() every time I call parent function or only inside overridden functions?

If we override parent class's method, we can use super() to avoid mention of parent class's name - that's clear.
But what about case, when we just use in subclass some function defined in parent class? What is preferable way: to use super().parent_method() or self.parent_method()? Or there's no difference?
class A:
def test1(self):
pass
def test_a(self):
pass
class B(A):
def test1(self):
super().test1() # That's clear.
def test_b(self):
# Which option is better, when I want to use parent's func here?
# 1) super().test_a()
# 2) self.test_a()
pass
Usually you will want to use self.test_a() to call an inherited method. However, in some rare situations you might want to use super().test_a() even though it seems to do the same thing. They're not equivalent, even though they have the same behavior in your example.
To explore the differences, lets make two versions of your B class, one with each kind of call, then make two C classes that further extend the B classes and override test_a:
class A(object):
def test_a(self):
return "A"
class B1(A):
def test_b(self):
return self.test_a() + "B"
class B2(A):
def test_b(self):
return super().test_a() + "B"
class C1(B1):
def test_a(self):
return "C"
class C2(B2):
def test_a(self):
return "C"
When you call the test_b() method on C1 and C2 instances you'll get different results, even though B1 and B2 behave the same:
>>> B1().test_b()
'AB'
>>> B2().test_b()
'AB'
>>> C1().test_b()
'CB'
>>> C2().test_b()
'AB'
This is because the super() call in B2.test_b tells Python that you want to skip the version of test_a in any more derived class and always call an implementation from a parent class. (Actually, I suppose it could be a sibling class in a multiple inheritance situation, but that's getting even more obscure.)
Like I said at the top, you usually want to allow a more-derived class like the Cs to override the behavior of the inherited methods you're calling in your less-derived class. That means that most of the time using self.whatever is the way to go. You only need to use super when you're doing something fancy.
Since B is an A, it has a member test_a. So you call it as
self.test_a()
B does not overwrite A.test_a so there is no need to use super() to call it.
Since B overwrites A.test1, you must explicitly name the method you want to call.
self.test1()
will call B.test1, while
super().test1()
will call A.test1.
Firstly both the ways super().test_a() and self.test_a() will result in execution of method test_a().
Since Class B does not override or overwrite test_a() I think use of self.test_a() will be much efficient as self is a mere reference to the current object which is there in memory.
As per documentation, super() results in creation of proxy object which contains other methods also. Owing to this reason I feel self will be the correct approach in your case.
If we override parent class's method, we can use super() to avoid
mention of parent class's name - that's clear.
actually super() is not syntactic sugar, its purpose is to invoke parent implementation of a certain method.
You have to use super() when you want to override a parent method, you don't have to use super() when instead you want to overwrite a method. The difference is that in the first case you want to add extra behavior (aka code execution) before or after the original implementation, in the second you want a completely different implementation.
You can't use self.method_name() in an override, the result will be a recursion error! (RuntimeError: maximum recursion depth exceeded)
Example:
class A:
def m(self):
print('A.m implementation')
class B(A):
def m(self):
super().m()
print('B.m implementation')
class C(A):
def m(self):
print('C.m implementation')
class D(A):
def m(self):
self.m()
a = A()
a.m()
b = B()
b.m()
c = C()
c.m()
d = D()
d.m()
Given a base class A, with a method m, B extends A by overriding m, C extends A by overwriting m, and D generates an error!
EDIT:
I just realized that you actually have 2 different methods (test_a and test_b). My answer is still valid, but regarding your specific scenario:
you should use self.test_a() unless you override/overwrite that method in your class B and you want to execute the original implementation... so we can say that calling super().test_a() or self.test_a() it's the same given that you'll never override/overwrite the original test_a() in your subclasses... however is a nonsense to use super() if not for an override/overwrite

Is there a way to implement methods like __len__ or __eq__ as classmethods?

It is pretty easy to implement __len__(self) method in Python so that it handles len(inst) calls like this one:
class A(object):
def __len__(self):
return 7
a = A()
len(a) # gives us 7
And there are plenty of alike methods you can define (__eq__, __str__, __repr__ etc.).
I know that Python classes are objects as well.
My question: can I somehow define, for example, __len__ so that the following works:
len(A) # makes sense and gives some predictable result
What you're looking for is called a "metaclass"... just like a is an instance of class A, A is an instance of class as well, referred to as a metaclass. By default, Python classes are instances of the type class (the only exception is under Python 2, which has some legacy "old style" classes, which are those which don't inherit from object). You can check this by doing type(A)... it should return type itself (yes, that object has been overloaded a little bit).
Metaclasses are powerful and brain-twisting enough to deserve more than the quick explanation I was about to write... a good starting point would be this stackoverflow question: What is a Metaclass.
For your particular question, for Python 3, the following creates a metaclass which aliases len(A) to invoke a class method on A:
class LengthMetaclass(type):
def __len__(self):
return self.clslength()
class A(object, metaclass=LengthMetaclass):
#classmethod
def clslength(cls):
return 7
print(len(A))
(Note: Example above is for Python 3. The syntax is slightly different for Python 2: you would use class A(object):\n __metaclass__=LengthMetaclass instead of passing it as a parameter.)
The reason LengthMetaclass.__len__ doesn't affect instances of A is that attribute resolution in Python first checks the instance dict, then walks the class hierarchy [A, object], but it never consults the metaclasses. Whereas accessing A.__len__ first consults the instance A, then walks it's class hierarchy, which consists of [LengthMetaclass, type].
Since a class is an instance of a metaclass, one way is to use a custom metaclass:
>>> Meta = type('Meta', (type,), {'__repr__': lambda cls: 'class A'})
>>> A = Meta('A', (object,), {'__repr__': lambda self: 'instance of class A'})
>>> A
class A
>>> A()
instance of class A
I fail to see how the Syntax specifically is important, but if you really want a simple way to implement it, just is the normal len(self) that returns len(inst) but in your implementation make it return a class variable that all instances share:
class A:
my_length = 5
def __len__(self):
return self.my_length
and you can later call it like that:
len(A()) #returns 5
obviously this creates a temporary instance of your class, but length only makes sense for an instance of a class and not really for the concept of a class (a Type object).
Editing the metaclass sounds like a very bad idea and unless you are doing something for school or to just mess around I really suggest you rethink this idea..
try this:
class Lengthy:
x = 5
#classmethod
def __len__(cls):
return cls.x
The #classmethod allows you to call it directly on the class, but your len implementation won't be able to depend on any instance variables:
a = Lengthy()
len(a)

Python multiple inheritance: Whats wrong doing it dynamically?

Based on this answer, of how __new__ and __init__ are supposed to work in Python,
I wrote this code to dynamically define and create a new class and object.
class A(object):
def __new__(cls):
class C(cls, B):
pass
self = C()
return self
def foo(self):
print 'foo'
class B(object):
def bar(self):
print 'bar'
a = A()
a.foo()
a.bar()
Basically, because the __new__ of A returns a dynamically created C that inherits A and B, it should have an attribute bar.
Why does C not have a bar attribute?
Resolve the infinite recursion:
class A(object):
def __new__(cls):
class C(cls, B):
pass
self = object.__new__(C)
return self
(Thanks to balpha for pointing out the actual question.)
Since there is no actual question in the question, I am going to take it literally:
Whats wrong doing it dynamically?
Well, it is practically unreadable, extremely opaque and non-obvious to the user of your code (that includes you in a month :P).
From my experience (quite limited, I must admit, unfortunately I don't have 20 years of programming under the belt), a need for such solutions indicates, that the class structure is not well defined, - means, there's almost always a better, more readable and less arcane way to do such things.
For example, if you really want to define base classes on the fly, you are better off using a factory function, that will return appropriate classes according to your needs.
Another take on the question:
Whats wrong doing it dynamically?
In your current implementation, it gives me a "maximum recursion depth exceeded" error. That happens, because A.__new__ calls itself from within itself indefinitely (since it inherits from itself and from B).
10: Inside A.__new__, "cls" is set to <class '.A'>. Inside the constructor you define a class C, which inherits from cls (which is actually A) and another class B. Upon instantiating C, its __new__ is called. Since it doesn't define its own __new__, its base class' __new__ is called. The base class just happens to be A.
20: GOTO 10
If your question is "How can I accomplish this" – this works:
class A(object):
#classmethod
def get_with_B(cls):
class C(B, cls):
pass
return C()
def foo(self):
print 'foo'
class B(object):
def bar(self):
print 'bar'
a = A.get_with_B()
a.foo()
a.bar()
If your question is "Why doesn't it work" – that's because you run into an infinite recursion when you call C(), which leads to A.__new__ being called, which again calls C() etc.

Categories