Найден более короткий способ сделать super(B, self).test()
-> bubble()
снизу.
(работает с множественным наследованием, не требует аргументов, корректно ведет себя с подклассами)
Решение состояло в том, чтобы использовать inspect.getmro(type(back_self))
(где back_self
- это self
от вызываемого абонента), затем итерировать его как cls
с method_name in cls.__dict__
и проверить, что ссылка на код, которая у нас есть, является единственной в этом классе (реализовано в find_class_by_code_object(self)
вложенной функции).
bubble()
может быть легко расширен с помощью *args, **kwargs
.
import inspect
def bubble(*args, **kwargs):
def find_class_by_code_object(back_self, method_name, code):
for cls in inspect.getmro(type(back_self)):
if method_name in cls.__dict__:
method_fun = getattr(cls, method_name)
if method_fun.im_func.func_code is code:
return cls
frame = inspect.currentframe().f_back
back_self = frame.f_locals['self']
method_name = frame.f_code.co_name
for _ in xrange(5):
code = frame.f_code
cls = find_class_by_code_object(back_self, method_name, code)
if cls:
super_ = super(cls, back_self)
return getattr(super_, method_name)(*args, **kwargs)
try:
frame = frame.f_back
except:
return
class A(object):
def test(self):
print "A.test()"
class B(A):
def test(self):
# instead of "super(B, self).test()" we can do
bubble()
class C(B):
pass
c = C()
c.test() # works!
b = B()
b.test() # works!
Если у кого-то есть идея получше, давайте послушаем.
Известная ошибка: (спасибо, удвоение) Если C.test = B.test
-> "бесконечная" рекурсия. Хотя это кажется нереальным для дочернего класса, чтобы на самом деле иметь метод, который был =
'от родительского класса.
Известная ошибка2: (спасибо doublep) Декорированные методы не будут работать (вероятно, нефиксированные, поскольку декоратор возвращает закрытие) ... Исправлена проблема декоратора с for _ in xrange(5)
:. .. frame = frame.f_back
- будет обрабатывать до 5 декораторов, увеличивать при необходимости. Я люблю Python!
Производительность в 5 раз хуже, чем super()
, но мы говорим о вызовах 200 КБ против миллиона вызовов в секунду, если это не входит в число ваших самых трудных циклов - нет причин для беспокойства.