开发者

Is there a way to identify an inherited method in Python?

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜