Инъекционный вызов функции после init с декоратором
Я пытаюсь найти лучший способ создать декоратор класса, который делает следующее:
- вводит несколько функций в украшенный класс
- вызывает вызов одной из этих функций после украшенного класса'
__init__называется
в настоящее время я просто сохраняю ссылку на "оригинал"__init__ метод и замена его на my __init__ это вызывает исходную и мою дополнительную функцию. Похоже на это:
orig_init = cls.__init__
def new_init(self, *args, **kwargs):
"""
'Extend' wrapped class' __init__ so we can attach to all signals
automatically
"""
orig_init(self, *args, **kwargs)
self._debugSignals()
cls.__init__ = new_init
есть ли лучший способ "увеличить" оригинал __init__ или ввести мой вызов где-то еще? Все что мне нужно это для моего self._debugSignals() вызывается через некоторое время после создания объекта. Я также хочу, чтобы это произошло автоматически, поэтому я подумал после __init__ хорошее место.
дополнительно смешанная. Примечания декоратора
возможно, стоит упомянуть некоторые предпосылки этого декоратора. Вы можете найти полный код здесь. Этот пункт декоратора автоматически прикрепиться к любым сигналам PyQt и напечатать когда они испущены. Декоратор отлично работает, когда я украшаю свои собственные подклассы QtCore.QObject, однако я недавно пытался автоматически украсить все дети QObject.
Я хотел бы иметь режим "отладки" в приложении, где я могу автоматически печатать все сигналы, чтобы убедиться, что вещи делают то, что я ожидаю. Я уверен, что это приведет к тоннам отладки, но я все равно хотелось бы посмотреть, что происходит.
проблема моя текущая версия оформителя вызывает segfault при замене QtCore.QObject.__init__. Я попытался отладить это, но код весь сгенерирован SIP, с которым у меня нет большого опыта.
Итак, мне было интересно, есть ли более безопасный, более питонический способ ввести вызов функции после __init__ и, надеюсь, избежать segfault.
2 ответов
на основе этот пост и ответ, альтернативный способ сделать это через пользовательский метакласс. Это будет работать следующим образом (проверено на Python 2.7):
# define a new metaclass which overrides the "__call__" function
class NewInitCaller(type):
def __call__(cls, *args, **kwargs):
"""Called when you call MyNewClass() """
obj = type.__call__(cls, *args, **kwargs)
obj.new_init()
return obj
# then create a new class with the __metaclass__ set as our custom metaclass
class MyNewClass(object):
__metaclass__ = NewInitCaller
def __init__(self):
print "Init class"
def new_init(self):
print "New init!!"
# when you create an instance
a = MyNewClass()
>>> Init class
>>> New init!!
основная идея заключается в том, что:
когда вы называете
MyNewClass()он ищет метакласс, находит, что вы определилиNewInitCallerметакласс
__call__вызывается функция.эта функция создает
MyNewClassэкземпляр с помощьюtype,экземпляр запускает свой собственный
__init__(печать "init class").затем мета-класс вызывает
вот решение для Python 3.х, на основе этот пост принят ответ. Также смотрите PEP 3115 Для справки, я думаю, что рассуждение интересно читать.
изменения в приведенном выше примере показаны с комментариями; единственное реальное изменение-способ определения метакласса, все остальные-тривиальные модификации 2to3.
# define a new metaclass which overrides the "__call__" function
class NewInitCaller(type):
def __call__(cls, *args, **kwargs):
"""Called when you call MyNewClass() """
obj = type.__call__(cls, *args, **kwargs)
obj.new_init()
return obj
# then create a new class with the metaclass passed as an argument
class MyNewClass(object, metaclass=NewInitCaller): # added argument
# __metaclass__ = NewInitCaller this line is removed; would not have effect
def __init__(self):
print("Init class") # function, not command
def new_init(self):
print("New init!!") # function, not command
# when you create an instance
a = MyNewClass()
>>> Init class
>>> New init!!