如何在python的子类中检测方法重载?

如何在python的子类中检测方法重载?,python,class,overriding,abstract-class,Python,Class,Overriding,Abstract Class,我有一个班,它是许多其他班的超级班。我想知道(在我的超类的init()中)子类是否重写了特定的方法 我试图用类方法来实现这一点,但结果是错误的: class Super: def __init__(self): if self.method == Super.method: print 'same' else: print 'different' @classmethod def method(cls):

我有一个班,它是许多其他班的超级班。我想知道(在我的超类的init()中)子类是否重写了特定的方法

我试图用类方法来实现这一点,但结果是错误的:

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

超类是否有办法知道子类是否重写了方法?

您可以将类的dict中的内容与方法中的函数进行比较 您可以从对象中检索- 下面的“detect_Overrided”(检测覆盖)功能就是这样做的-诀窍是通过 “父类”作为其名称,就像调用“super”一样- 否则,从父类本身检索属性并不容易 而不是子类:

# -*- 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
编辑更改代码,使其与classmethods配合使用: 如果在父类上检测到classmethod,则在继续之前提取底层函数

--
不必硬编码类名的另一种方法是遵循实例的类(
self.\uuuuu class\uuuuu
)方法解析顺序(由
\uuuuu mro\uuu
属性给出)并沿继承链搜索每个类中定义的方法和属性的副本。

您可以使用自己的装饰器。但这是一个技巧,只适用于您控制实现的类

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

通过比较实例字典的公共子集和基类本身,这似乎是最简单和足够的,例如:

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()
运行并给出:

['method1']
[]
作为对答案的回答,由于我还没有足够的学分对其进行评论,除非您将
im_func
替换为
im_func
,并且在python 3.4(很可能是以后)中也不起作用,因为函数不再具有
\u func
属性,只具有绑定方法

编辑:这是原始问题的解决方案(它适用于2.7和3.4,我假设所有其他版本都在这两个版本之间):

以下是输出:

same
different
same

不确定这是否是您正在寻找的,但在我寻找类似解决方案时,它帮助了我

class A:
    def fuzz(self):
        pass

class B(A):
    def fuzz(self):
        super().fuzz()

assert 'super' in B.__dict__['fuzz'].__code__.co_names

我使用以下方法来确定给定的绑定方法是被重写还是源自父类

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))
结果:

Overridden
Original
True
False

当然,要使其工作,必须在基类中定义方法。

如果要在Python 3中检查重写的实例方法,可以使用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

现在Base()和Sub2()都应该打印“相同”,而Sub1()打印“不同”。classmethod decorator使第一个参数绑定到self的类型,并且由于子类的类型根据定义不同于其基类,因此两个类方法将进行不相等的比较。通过将该方法设置为实例方法并使用self的类型,可以将一个普通函数与另一个普通函数进行比较,假设函数(如果使用的是Python2,则在本例中为未绑定的方法)与其自身比较相等(在C Python实现中是这样做的),将生成所需的行为。

您可以通过查看函数句柄是否指向超类函数来检查函数是否已被重写。子类对象中的函数处理程序指向超类函数或子类中被重写的函数。例如:

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

如果函数句柄未指向Super类中的函数,则它已被重写。

您还可以检查是否从其父类重写了某些内容,而不知道使用Super所涉及的任何类:

A类:
def模糊(自):
通过
B(A)类:
def模糊(自):
super().fuzz()
C(A)类:
通过
>>b=b();c=c()
>>>fuzz是super(b.\u类,b).fuzz.\u函数__
假的
>>>fuzz是super(c.\u class\u,c).fuzz.\u func__
真的
有关更多信息,请参阅

一般功能:

def覆盖(实例、函数名称):
return getattr(实例.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu函数名)不是getattr(超级__
>>>覆盖(b,“模糊”)
真的
>>>覆盖(c,“模糊”)
假的

你能说几句你为什么要这么做吗?没有什么会马上想到的,但是如果你为你的方法包含docstring,那么当方法被重写时,它们就会被重写。因此你应该能够用
MyClass.methodname.\uu doc\uu
来跟踪它。但是我发现这个解决方案非常粗糙,这就是我为什么要这么做的原因我不是把它作为答案发布的基本上我想让超类将这些方法定义为“pass”,并有一个单独的方法(实际上是init)调用这些函数。我想让init放在print语句中,说明我们正在启动或结束函数,但如果该函数仍然为null,这些语句看起来不合适,我想删除它们。@Brian为什么不让这些
print
语句成为子类而不是超级类中方法的一部分呢ass?超级类的部分目标是消除将这些常用项添加到子类的需要。有一个超级类和许多子类。我只是朝着稍微不同的方向走,用@native装饰我的方法,并假设子类不会。我想我会这样做,因为我对子类没有太多控制权-类。它工作得很好,感谢您的帮助!或者更简单的put
Super.method。_original=True
Super()
类的主体,然后检查
hasattr(self.method,'.\u original')
。注意:在cython中(至少在python 3.4中),
self.method.\uuuu-code\uuuu
不存在,但可以在干净的Python中工作。由于某种原因,
dir(self.method)
self中缺少
\uuu-code
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