I want to tell inherited methods apart from overloaded or newly defined methods. Is that possible with Python?
Example:
class A(object):
def spam(self):
print 'A spam'
def ham(self):
print 'A ham'
class B(A):
def spam(self):
print 'Overloaded spam'
def eggs(self):
print 'Newly defined eggs'
Desired functionality:
>>> magicmethod(B.spam)
'overloaded'
>>> magicmethod(B.ham)
'inherited'
>>> magicmethod(B.eggs)
'newly defined'
Is there a "magic method" like in the example, or some way to tell those types of method implementations apart?开发者_运维百科
I'm not sure it's a good idea, but you can probably do it by using hasattr
and __dict__
.
def magicmethod(clazz, method):
if method not in clazz.__dict__: # Not defined in clazz : inherited
return 'inherited'
elif hasattr(super(clazz), method): # Present in parent : overloaded
return 'overloaded'
else: # Not present in parent : newly defined
return 'newly defined'
If you know the ancestor, you can simply test:
>>> B.spam == A.spam
False
>>> B.ham == A.ham
True
And to find list of all base classes, look here: List all base classes in a hierarchy of given class?
I shall also point that if you need this, your class design is probably wrong. You should not care about such things in OOP (unless you're creating an object inspector or something like that).
A general way will be (python 2.*):
def _getclass(method):
try:
return method.im_class
except AttributeError:
return method.__class__
def magicmethod(method):
method_cls = _getclass(method)
if method.__name__ not in method_cls.__dict__:
return 'inherited'
for cls in method_cls.__mro__[1:]:
if method.__name__ in cls.__dict__:
return 'overloaded'
return 'newly defined'
__test__ = {"example": """
>>> class A(object):
... def spam(self):
... print 'A spam'
... def ham(self):
... print 'A ham'
>>> class B(A):
... def spam(self):
... print 'Overloaded spam'
... def eggs(self):
... print 'Newly defined eggs'
>>> magicmethod(B.spam)
'overloaded'
>>> magicmethod(B.ham)
'inherited'
>>> magicmethod(B.eggs)
'newly defined'
>>> magicmethod(B.__init__)
'inherited'
"""}
For new-style classes, you have a method mro()
, returning the method resulution order list. [0]
is the class itself.
So you could do
>>> any(hasattr(i, 'ham') for i in B.mro()[1:])
True
>>> any(hasattr(i, 'spam') for i in B.mro()[1:])
True
>>> any(hasattr(i, 'eggs') for i in B.mro()[1:])
False
So eggs
is newly defined.
>>> any(getattr(B, 'ham') == getattr(i, 'ham', None) for i in B.mro()[1:])
True
>>> any(getattr(B, 'spam') == getattr(i, 'spam', None) for i in B.mro()[1:])
False
So ham
is inherited.
With these, you can craft your own heuristics.
Expanding on hamstergene's answer; a class stores its base classes in the class variable __bases__
.
So:
>>> B.spam == B.__bases__[0].spam
False
>>> B.ham == B.__bases__[0].ham
True
>>> B.eggs == B.__bases__[0].eggs
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'A' has no attribute 'eggs'
>>> hasattr(B,"eggs")
True
>>> hasattr(B.__bases__[0],"eggs")
False
精彩评论