Как обнаружить перегрузку метода в подклассах в Python? - PullRequest
18 голосов
/ 24 февраля 2012

У меня есть класс, который является суперклассом для многих других классов.Я хотел бы знать (в 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

Есть ли какой-нибудь способ для суперкласса узнать, переопределил ли подкласс метод?

Ответы [ 5 ]

9 голосов
/ 25 февраля 2012

Вы можете использовать свой собственный декоратор.Но это хитрость и будет работать только на классах, где вы управляете реализацией.

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
5 голосов
/ 02 июля 2015

В ответ на ответ https://stackoverflow.com/a/9437273/1258307,, поскольку у меня еще недостаточно кредитов, чтобы комментировать его, он не будет работать в Python 3, если вы не замените im_func на __func__, а также не будет работать вPython 3.4 (и, скорее всего, вперед), поскольку функции больше не имеют атрибута __func__, только связанные методы.

РЕДАКТИРОВАТЬ: Вот решение исходного вопроса (который работал на 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

А вот вывод:

same
different
same
5 голосов
/ 09 июня 2013

Это кажется наиболее простым и достаточным для этого, сравнивая общее подмножество словарей экземпляра и самого базового класса, например ::100100

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']
[]
3 голосов
/ 25 февраля 2012

Вы можете сравнить все, что есть в классе __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 изменил код, чтобы нормально работать стакже методы класса: если он обнаруживает метод класса в родительском классе, извлекает базовую функцию перед продолжением.

- Еще один способ сделать это без необходимости жесткого кодирования имени класса состоит в том, чтобы следовать за экземпляром экземпляра.порядок разрешения методов класса (self.__class__) (заданный атрибутом __mro__) и поиск дубликатов методов и атрибутов, определенных в каждом классе, по цепочке наследования.

0 голосов
/ 29 октября 2018

Не уверен, что это то, что вы ищете, но мне помогло, когда я искал подобное решение.

class A:
    def fuzz(self):
        pass

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

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