Как обнаружить перегрузку метода в подклассах в python?
У меня есть класс, который является суперклассом для многих других классов. Я хотел бы знать (в 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
Есть ли способ суперкласса узнать, переопределил ли подкласс класс?
Ответы
Ответ 1
Вы можете использовать свой собственный декоратор. Но это трюк и будет работать только на классах, где вы контролируете реализацию.
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
В ответ на fooobar.com/questions/4703343/..., так как у меня пока нет комментариев, чтобы прокомментировать это, он не будет работать под python 3, если вы не замените im_func
на __func__
и также не сможете работать в python 3.4 (и, скорее всего, дальше), поскольку функции больше не имеют атрибута __func__
, только связанные методы.
EDIT: Здесь решение первоначального вопроса (который работал с 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
Ответ 3
Проще всего и достаточно сделать это, сравнивая общее подмножество словарей экземпляра и самого базового класса, например:
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']
[]
Ответ 4
Вы можете сравнить все, что находится в классе __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
отредактируйте измененный код, чтобы нормально работать с классами: если он обнаруживает метод класса в родительском классе, извлекает основную функцию перед продолжением.
- Еще один способ сделать это, не требуя жесткого кода имени класса, - следовать self.__class__
разрешения порядка экземпляра класса (self.__class__
) (заданному атрибутом __mro__
) и искать дубликаты методов и атрибутов, определенных в каждый класс по цепочке наследования.