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

时间:2021-06-11 00:07:48

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.

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

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?

有没有办法让超类知道子类是否覆盖了一个方法?

4 个解决方案

#1


8  

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

#2


5  

In reply to answer https://*.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.

在回答https://*.com/a/9437273/1258307的回复中,因为我还没有足够的信用评论它,除非你用__func__替换im_func并且也无法工作,否则它将无法在python 3下工作在python 3.4中(很可能是向前),因为函数不再具有__func__属性,只有绑定方法。

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

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

    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

#3


4  

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']
[]

#4


3  

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:

你可以将类__dict__中的任何东西与你可以从对象中检索的方法中的函数进行比较 - “detect_overriden”函数可以做到这一点 - 诀窍是传递“父类”作为其名称,就像在调用“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

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.

编辑更改的代码以使用classmethods也可以正常工作:如果它在父类上检测到类方法,则在继续之前提取基础函数。

-- 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.

- 另一种方法是在不必对类名进行硬编码的情况下,遵循实例的类(self .__ class__)方法解析顺序(由__mro__属性给出),并搜索在中定义的方法和属性的副本。继承链中的每个类。

#1


8  

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

#2


5  

In reply to answer https://*.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.

在回答https://*.com/a/9437273/1258307的回复中,因为我还没有足够的信用评论它,除非你用__func__替换im_func并且也无法工作,否则它将无法在python 3下工作在python 3.4中(很可能是向前),因为函数不再具有__func__属性,只有绑定方法。

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

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

    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

#3


4  

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']
[]

#4


3  

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:

你可以将类__dict__中的任何东西与你可以从对象中检索的方法中的函数进行比较 - “detect_overriden”函数可以做到这一点 - 诀窍是传递“父类”作为其名称,就像在调用“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

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.

编辑更改的代码以使用classmethods也可以正常工作:如果它在父类上检测到类方法,则在继续之前提取基础函数。

-- 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.

- 另一种方法是在不必对类名进行硬编码的情况下,遵循实例的类(self .__ class__)方法解析顺序(由__mro__属性给出),并搜索在中定义的方法和属性的副本。继承链中的每个类。