Инъекционный вызов функции после init с декоратором

Я пытаюсь найти лучший способ создать декоратор класса, который делает следующее:

  1. вводит несколько функций в украшенный класс
  2. вызывает вызов одной из этих функций после украшенного класса'__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!!

основная идея заключается в том, что:

  1. когда вы называете MyNewClass() он ищет метакласс, находит, что вы определили NewInitCaller

  2. метакласс __call__ вызывается функция.

  3. эта функция создает MyNewClass экземпляр с помощью type,

  4. экземпляр запускает свой собственный __init__ (печать "init class").

  5. затем мета-класс вызывает


вот решение для 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!!