Related
Is there a way for a child class method to prevent the parent from calling the another child method, but instead call the parent's method?
Here's the code sample I'm referring to:
class A:
def f(self):
return 'Af'
def ff(self, var):
return var + 'ff ' + self.f()
class B(A):
def f(self):
return 'Bf'
def ff(self):
return super().ff('B')
print(B().ff())
The output is Bff Bf
I want it to be: Bff Af
In other words, when B.f() it's called, I would like to redirect the call to A.f() if the calling class is A.
And if the calling class is B then call B.f().
Is this possible?
The problem here is that you haven't designed A for inheritance. For method overriding to work cleanly and without unpredictable side effects, you need to carefully think about and document what methods depend on what other methods.
Also, you have a serious Liskov substitution violation in the fact that B.ff takes 0 arguments when A.ff takes one.
You have three primary options here. One is to use composition instead:
class B:
def __init__(self):
self.a = A()
def f(self):
return 'Bf'
def ff(self):
return self.a.ff('B')
Now B.f has no effect on either class's ff. Given the mismatching A.ff and B.ff signatures, having B inherit from A is probably a bad idea even if you clean up A to support inheritance better.
Option 2 is to decide that f is a hook for customizing ff. In this case, you keep the current A implementation and behavior, and if child classes want to customize ff separately, they need to override that in a way that doesn't depend on A.ff:
class B(A):
def f(self):
return 'Bf'
def ff(self):
return 'Bff ' + super().f()
Option 3 is to decide that overriding f should not affect ff. In that case, A.ff needs to invoke A.f in a way that is not affected by overrides. For example, A.ff could call f as A.f instead of self.f:
class A:
def f(self):
return 'Af'
def ff(self, var):
return var + 'ff ' + A.f(self)
Then your existing B will work as intended. This option is probably the runner-up after option 1.
You can easily do it by not using inheritance (and using composition instead):
class A:
def f(self):
return 'Af'
def ff(self, var):
return var + 'ff ' + self.f()
class B:
def __init__(self):
self.a = A()
def f(self):
return 'Bf'
def ff(self):
return self.a.ff('B')
print(B().ff())
Output:
Bff Af
I have 2 classes: A and B, both of them instantiated by __new__ with different set of arguments (like a for A, and foo, bar for B). Now I want to implement class C, inherited from A and B, and instantiate it with 3 args: a, foo, bar, passing correspondent arguments to super classes __new__, but things goes wrong from here.
If we have no arguments I just call super(C, cls).__new__() and object of class C successfully created (it calls both A.__new__() and B.__new__() and combines it somehow). But how to do it 'by hands'? So I want to pass a to A.__new__, foo, bar to B.__new__ and combine somehow returned instances (is this right way to get object of class C at the end?).
Anyway I can't do both.
Fist - calling A.__new__ raises incorrect number of arguments exception in o = super(A, cls).__new__(cls) in A.__new__() (but A can be instantiated as standalone)
Second - I have no idea how to combine even successfully instantiated object of classes A and B into object of class C.
So could please someone to explain what is going on here?
class A(object):
def __new__(cls, a):
o = super(A, cls).__new__(cls)
setattr(o, 'a', a)
return o
class B(object):
def __new__(cls, foo, bar):
o = super(B, cls).__new__(cls)
setattr(o, 'foo', foo)
setattr(o, 'bar', bar)
return o
print A(1) # ok, <__main__.A object at 0x00000000022F1630>
print B(2,3) # ok, <__main__.B object at 0x00000000022F1630>
class C(A,B):
def __new__(cls, a, foo, bar):
o1 = A.__new__(cls, a) #fail - exception while calling super.new in A
o2 = B.__new__(cls, foo, bar) #fail - exception while calling super.new in A
# return # What? How to combine o1 o2 even if they are created succesfuly?
# # return super(C, cls).__new__(cls, ?????)
print C(1,2,3)
The method __new__ is what creates your instance, you should not call super(...).__new__ multiple times as it would create multiple instances.
What you want to do it use __init__ which initializes your already created instance.
class A(object):
def __init__(self, a):
self.a = a
class B(object):
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
class C(A, B):
def __init__(self, a, foo, bar):
A.__init__(self, a)
B.__init__(self, foo, bar)
In particular, I want to point out that it is not true that on multiple inheritance Python will call both A.__new__ andB.__new__ and "combine somehow". Have a look at this code
class A(object):
def __new__(*args):
print('A.__new__ was called')
return type(*args) # This is what ultimately creates every object in Python
class B(object):
def __new__(*args):
print('B.__new__ was called')
return type(*args)
# As expected the following is printed when instances are created
a = A() # Prints 'A.__new__ was called'
b = B() # Prints 'B.__new__ was called'
class C(A, B):
pass
c = C() # Prints 'A.__new__ was called'
So we observe that B.__new__ was never called. On multiple inheritance, Python will inherit the method from the left-most class that has this method. In this case, C inherited A.__new__.
I have a class that is a super-class to many other classes. I would like to know (in the __init__() of my super-class) if the subclass has overridden a specific method.
I tried to accomplish this with a class method, but the results were wrong:
class Super:
def __init__(self):
if self.method == Super.method:
print 'same'
else:
print 'different'
#classmethod
def method(cls):
pass
class Sub1(Super):
def method(self):
print 'hi'
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
>>> same
>>> different
>>> different
Is there any way for a super-class to know if a sub-class has overridden a method?
It seems simplest and sufficient to do this by comparing the common subset of the dictionaries of an instance and the base class itself, e.g.:
def detect_overridden(cls, obj):
common = cls.__dict__.keys() & obj.__class__.__dict__.keys()
diff = [m for m in common if cls.__dict__[m] != obj.__class__.__dict__[m]]
print(diff)
def f1(self):
pass
class Foo:
def __init__(self):
detect_overridden(Foo, self)
def method1(self):
print("Hello foo")
method2=f1
class Bar(Foo):
def method1(self):
print("Hello bar")
method2=f1 # This is pointless but not an override
# def method2(self):
# pass
b=Bar()
f=Foo()
Runs and gives:
['method1']
[]
If you want to check for an overridden instance method in Python 3, you can do this using the type of self:
class Base:
def __init__(self):
if type(self).method == Base.method:
print('same')
else:
print('different')
def method(self):
print('Hello from Base')
class Sub1(Base):
def method(self):
print('Hello from Sub1')
class Sub2(Base):
pass
Now Base() and Sub2() should both print "same" while Sub1() prints "different". The classmethod decorator causes the first parameter to be bound to the type of self, and since the type of a subclass is by definition different to its base class, the two class methods will compare as not equal. By making the method an instance method and using the type of self, you're comparing a plain function against another plain function, and assuming functions (or unbound methods in this case if you're using Python 2) compare equal to themselves (which they do in the C Python implementation), the desired behavior will be produced.
You can use your own decorator. But this is a trick and will only work on classes where you control the implementation.
def override(method):
method.is_overridden = True
return method
class Super:
def __init__(self):
if hasattr(self.method, 'is_overridden'):
print 'different'
else:
print 'same'
#classmethod
def method(cls):
pass
class Sub1(Super):
#override
def method(self):
print 'hi'
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
>>> same
>>> different
>>> same
In reply to answer https://stackoverflow.com/a/9437273/1258307, since I don't have enough credits yet to comment on it, it will not work under python 3 unless you replace im_func with __func__ and will also not work in python 3.4(and most likely onward) since functions no longer have the __func__ attribute, only bound methods.
EDIT: Here's the solution to the original question(which worked on 2.7 and 3.4, and I assume all other version in between):
class Super:
def __init__(self):
if self.method.__code__ is Super.method.__code__:
print('same')
else:
print('different')
#classmethod
def method(cls):
pass
class Sub1(Super):
def method(self):
print('hi')
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
And here's the output:
same
different
same
You can compare whatever is in the class's __dict__ with the function inside the method
you can retrieve from the object -
the "detect_overriden" functionbellow does that - the trick is to pass
the "parent class" for its name, just as one does in a call to "super" -
else it is not easy to retrieve attributes from the parentclass itself
instead of those of the subclass:
# -*- coding: utf-8 -*-
from types import FunctionType
def detect_overriden(cls, obj):
res = []
for key, value in cls.__dict__.items():
if isinstance(value, classmethod):
value = getattr(cls, key).im_func
if isinstance(value, (FunctionType, classmethod)):
meth = getattr(obj, key)
if not meth.im_func is value:
res.append(key)
return res
# Test and example
class A(object):
def __init__(self):
print detect_overriden(A, self)
def a(self): pass
#classmethod
def b(self): pass
def c(self): pass
class B(A):
def a(self): pass
##classmethod
def b(self): pass
edit changed code to work fine with classmethods as well:
if it detects a classmethod on the parent class, extracts the underlying function before proceeding.
--
Another way of doing this, without having to hard code the class name, would be to follow the instance's class ( self.__class__) method resolution order (given by the __mro__ attribute) and search for duplicates of the methods and attributes defined in each class along the inheritance chain.
I'm using the following method to determine if a given bound method is overridden or originates from the parent class
class A():
def bla(self):
print("Original")
class B(A):
def bla(self):
print("Overridden")
class C(A):
pass
def isOverriddenFunc(func):
obj = func.__self__
prntM = getattr(super(type(obj), obj), func.__name__)
return func.__func__ != prntM.__func__
b = B()
c = C()
b.bla()
c.bla()
print(isOverriddenFunc(b.bla))
print(isOverriddenFunc(c.bla))
Result:
Overridden
Original
True
False
Of course, for this to work, the method must be defined in the base class.
You can also check if something is overridden from its parents, without knowing any of the classes involved using super:
class A:
def fuzz(self):
pass
class B(A):
def fuzz(self):
super().fuzz()
class C(A):
pass
>>> b = B(); c = C()
>>> b.__class__.fuzz is super(b.__class__, b).fuzz.__func__
False
>>> c.__class__.fuzz is super(c.__class__, c).fuzz.__func__
True
See this question for some more nuggets of information.
A general function:
def overrides(instance, function_name):
return getattr(instance.__class__, function_name) is not getattr(super(instance.__class__, instance), function_name).__func__
>>> overrides(b, "fuzz")
True
>>> overrides(c, "fuzz")
False
You can check to see if the function has been overridden by seeing if the function handle points to the Super class function or not. The function handler in the subclass object points either to the Super class function or to an overridden function in the Subclass. For example:
class Test:
def myfunc1(self):
pass
def myfunc2(self):
pass
class TestSub(Test):
def myfunc1(self):
print('Hello World')
>>> test = TestSub()
>>> test.myfunc1.__func__ is Test.myfunc1
False
>>> test.myfunc2.__func__ is Test.myfunc2
True
If the function handle does not point to the function in the Super class, then it has been overridden.
Not sure if this is what you're looking for but it helped me when I was looking for a similar solution.
class A:
def fuzz(self):
pass
class B(A):
def fuzz(self):
super().fuzz()
assert 'super' in B.__dict__['fuzz'].__code__.co_names
The top-trending answer and several others use some form of Sub.method == Base.method. However, this comparison can return a false negative if Sub and Base do not share the same import syntax. For example, see discussion here explaining a scenario where issubclass(Sub, Base) -> False.
This subtlety is not apparent when running many of the minimal examples here, but can show up in a more complex code base. The more reliable approach is to compare the method defined in the Sub.__bases__ entry corresponding to Base because __bases__ is guaranteed to use the same import path as Sub
import inspect
def method_overridden(cls, base, method):
"""Determine if class overriddes the implementation of specific base class method
:param type cls: Subclass inheriting (and potentially overriding) the method
:param type base: Base class where the method is inherited from
:param str method: Name of the inherited method
:return bool: Whether ``cls.method != base.method`` regardless of import
syntax used to create the two classes
:raises NameError: If ``base`` is not in the MRO of ``cls``
:raises AttributeError: If ``base.method`` is undefined
"""
# Figure out which base class from the MRO to compare against
base_cls = None
for parent in inspect.getmro(cls):
if parent.__name__ == base.__name__:
base_cls = parent
break
if base_cls is None:
raise NameError(f'{base.__name__} is not in the MRO for {cls}')
# Compare the method implementations
return getattr(cls, method) != getattr(base_cls, method)
In python, is there a way, when initializing a Class, to change the superclass in function of the value of a class attribute? Here's an example of what I want to do. First I have theses classes:
class A(object):
pass
class B(A):
# extend and override class A
pass
class C(A or B):
# extend and override class A
pass
Secondly, I want to create other classes that inherit from Class C but in some cases I want C to inherit from A and on other cases, inherit from B:
class D(C):
# C inherit only from A
from_B = False
class E(C):
# C inherit from B because attribute from_B = True
from_B = True
I tried with metaclass but that was setting the base class of C (to A or B) for all subclasses (D, E, ...) at the initialization of the first subclass. So, if the first subclass to be initialize had from_B = True, all subclasses of C had C(B) as parent whatever from_B was set. My code was something like this:
class MetaC(type):
def __new__(cls, name, bases, attrs):
if C in bases and getattr(attrs, 'from_B', False):
C.__bases__[C.__bases__.index(A)] = B
return super(MetaC, cls).__new__(cls, name, bases, attrs)
class C(A):
__metaclass__ = MetaC
My goal is to avoid the duplication of the code of the C class and keeping the possibility to have or not the added functionalities of the B class. I should mention that I don't have control on A and B classes.
UPDATE
I think I got it with this metaclass (code is a bit rough at the moment):
class MetaC(type):
def __new__(cls, name, bases, attrs):
for base in bases:
if base.__name__ == 'C':
if attrs.has_key('from_B'):
list_bases = list(base.__bases__)
list_bases[list_bases.index(A)] = B
base.__bases__ = tuple(list_bases)
elif B in base.__bases__:
list_bases = list(base.__bases__)
list_bases[list_bases.index(B)] = A
base.__bases__ = tuple(list_bases)
break
return super(MetaC, cls).__new__(cls, name, bases, attrs)
UPDATE 2
This solution doesn't work because I'm always modifying the base class C. So, when a subclass is instanciated it will use the C class in it's current state.
I ended by using cooperative multiple inheritance. It works fine. The only drawback is that we need to be sure that for methods that need to be call on many parent classes (like methods that are present in A and B and C), there's a super() call in each method definitions of each classes and that they have the same calling signature in every case. Fortunately for me my B classes respect this.
Example:
class A(object):
some_method(arg1, arg2, karg1=None):
do_some_stuff(arg1, arg2, karg1)
class B(A):
# extend and override class A
some_method(arg1, arg2, karg1=None):
super(B, self).some_method(arg1, arg2, karg1)
do_more_stuff(arg1, arg2, karg1)
class C(A, B):
# extend and override class A
some_method(arg1, arg2, karg1=None):
do_other_stuff(arg1, arg2, karg1)
super(C, self).some_method(arg1, arg2, karg1)
This way, when some_method will be call from C or C childrens, all theses calls will be made in this order:
C.some_method
A.some_method
B.some_method
Check The wonders of cooperative inheritance for more info on the subject.
This looks so painful, you have to consider composition/delegation instead of contorting inheritance this way. What do you think of something like this?
class A(object):
def from_B(self):
return False
class B(object):
def from_B(self):
return True
class C(object):
pass
class PolyClass(object):
def __init__(self, *args):
self.delegates = [c() for c in args[::-1]]
def __getattr__(self, attr):
for d in self.delegates:
if hasattr(d, attr):
return getattr(d,attr)
raise AttributeError(attr + "? what the heck is that?")
def __repr__(self):
return "<instance of (%s)>" % ','.join(d.__class__.__name__
for d in self.delegates[::-1])
pc1 = PolyClass(A,B)
pc2 = PolyClass(A,C)
pc3 = PolyClass(B,C)
for p in (pc1,pc2,pc3):
print p, p.from_B()
print pc1.from_C()
Prints:
<instance of (A,B)> True
<instance of (A,C)> False
<instance of (B,C)> True
Traceback (most recent call last):
File "varying_delegation.py", line 33, in <module>
print pc1.from_C()
File "varying_delegation.py", line 21, in __getattr__
raise AttributeError(attr + "? what the heck is that?")
AttributeError: from_C? what the heck is that?
EDIT:
Here's how to take the not-in-your-control classes A and B, and create custom C classes that look like they extend either an A or a B:
# Django admin classes
class A(object):
def from_B(self):
return False
class B(A):
def from_B(self):
return True
# Your own class, which might get created with an A or B instance
class C(object):
def __init__(self, obj):
self.obj = obj
def __getattr__(self, attr):
return getattr(self.obj, attr)
# these are instantiated some way, not in your control
a,b = A(), B()
# now create different C's
c1 = C(a)
c2 = C(b)
print c1.from_B()
print c2.from_B()
prints:
False
True
And to create your subclasses D and E, create an interim subclass of C (I called it SubC cause I lack imagination), which will auto-init the C superclass with a specific global variable, either a or b.
# a subclass of C for subclasses pre-wired to delegate to a specific
# global object
class SubC(C):
c_init_obj = None
def __init__(self):
super(SubC,self).__init__(self.c_init_obj)
class D(SubC): pass
class E(SubC): pass
# assign globals to C subclasses so they build with the correct contained
# global object
D.c_init_obj = a
E.c_init_obj = b
d = D()
e = E()
print d.from_B()
print e.from_B()
Again, prints:
False
True
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.