Определить, есть ли функция super () с декоратором? - PullRequest
2 голосов
/ 24 февраля 2012

Я определяю несколько классов, предназначенных для использования для множественного наследования, например:

class A:
    def __init__(self, bacon = None, **kwargs):
        self.bacon = bacon
        if bacon is None:
            self.bacon = 100
        super().__init__(**kwargs)
class Bacon(A):
    def __init__(self, **kwargs):
        """Optional: bacon"""
        super().__init__(**kwargs)

class Eggs(A):
    def __init__(self, **kwargs):
        """Optional: bacon"""
        super().__init__(**kwargs)


class Spam(Eggs, Bacon):
    def __init__(self, **kwargs):
        """Optional: bacon"""
        super().__init__(**kwargs)

Однако у меня есть несколько классов (например, возможно Bacon, A и Spam,но не Eggs), которые заботятся о том, когда их свойство bacon изменяется.Им не нужно изменять значение, только чтобы узнать, что такое новое значение, например, событие.Из-за природы множественного наследования, которую я настроил, это означало бы необходимость уведомления суперкласса об изменении (если это волнует).

Я знаю, что это возможно, если я передам имя классаМетод декоратор, или если я использую декоратор класса.Я не хочу иметь прямую ссылку на собственный класс, создавать множество декораторов над каждым классом или заставлять методы иметь одно и то же имя, поскольку ни один из них не звучит слишком питонно.

Я надеялся получить синтаксис, который будет выглядеть примерно так:

    @on_change(bacon)
    def on_bacon_change(self, bacon):
        # read from old/new bacon
        make_eggs(how_much = bacon)

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

  • Можно ли проверить, есть ли у суперкласса метод с этим декоратором?

  • Если это неосуществимо, есть ли альтернативы прохождению таких событий по цепочке множественного наследования?

EDIT:

Фактический вызов функции в Спаме будет выполняться в A с использованием @property и @bacon.setter, так как это будет самый верхний класс, который инициализирует bacon.Как только он узнает, какую функцию вызывать на self, проблема заключается только в распространении вызова по цепочке MI.

EDIT 2:

Если я переопределяюс атрибутом @bacon.setter. Можно ли определить, имеет ли класс super () установщик для bacon?

Ответы [ 2 ]

2 голосов
/ 24 февраля 2012

То, к чему вы обращаетесь, вероятно, будет хорошо соответствовать более полной структуре сигналов и т. Д. - возможно, даже пригласит для Аспектно-ориентированного программирования.

Однако, не углубляясь в это, метакласс и декоратор могут сделать то, что вы просите - я придумал это, я надеюсь, что они сработают для вас.

Если выхотел бы развить это во что-то надежное и пригодное для использования, напишите мне - если ничего подобного не существует, стоило бы сохранить пакет утилит в pipy для этого.

def setattr_wrapper(cls):
    def watcher_setattr(self, attr, val):
        super(cls, self).__setattr__(attr, val)
        watched = cls.__dict__["_watched_attrs"]
        if attr in watched:
            for method in watched[attr]:
                getattr(self, method)(attr, val)
    return watcher_setattr

class AttrNotifier(type):
    def __new__(metacls, name, bases, dct):
        dct["_watched_attrs"] = {}
        for key, value in dct.items():
            if hasattr(value, "_watched_attrs"):
                for attr in getattr(value, "_watched_attrs"):
                    if not attr in dct["_watched_attrs"]:
                        dct["_watched_attrs"][attr] = set()
                    dct["_watched_attrs"][attr].add(key)
        cls = type.__new__(metacls, name, bases, dct)
        cls.__setattr__ = setattr_wrapper(cls)
        return cls


def on_change(*args):
    def decorator(meth):
        our_args = args
        #ensure that this decorator is stackable
        if hasattr(meth, "_watched_attrs"):
            our_args = getattr(meth, "_watched_attrs") + our_args
        setattr(meth, "_watched_attrs", our_args)
        return meth
    return decorator

# from here on, example of use:
class A(metaclass=AttrNotifier):
    @on_change("bacon")
    def bacon_changed(self, attr, val):
        print ("%s changed in %s to %s" % (attr, self.__class__.__name__, val)) 


class Spam(A):
    @on_change("bacon", "pepper")
    def changed(self, attr, val):
        print ("%s changed in %s to %s" % (attr, self.__class__.__name__, val)) 

a = A()
a.bacon = 5

b = Spam()
b.pepper = 10
b.bacon = 20

(протестировано в Python 3.2 иPython 2.6 - изменение объявления класса «A» для синтаксиса метакласса Python 2)

edit - несколько слов о том, что делается Вот что происходит: метакласс выбирает все отмеченные методыс помощью декоратора «on_close» и зарегистрируйтесь в словаре класса - этот словарь называется _watched_attrs, и к нему можно обращаться как к обычному атрибуту класса.

Другая вещь, которую метакласс делает, - переопределениеметод __setattr__ для класа после его создания.Этот новый __setattr__ просто устанавливает атрибут, а затем проверяет словарь _wacthed_attrs, есть ли в этом классе какие-либо методы, зарегистрированные для вызова при изменении только что измененного атрибута - если так, он вызывает его.

Дополнительный уровень косвенности около watcher_setattr (это функция, которая становится __setattr__ каждого класса, так что вы можете зарегистрировать различные атрибуты, которые будут наблюдаться в каждом классе в цепочке наследования - все классы имеют доступ независимо _watched_attrs словари. Если бы не это, был бы соблюден только самый специализированный класс в цепочке наследования _watched_attrs.

0 голосов
/ 24 февраля 2012

Вы ищете python properties :

http://docs.python.org/library/functions.html#property

Поиск в Google по запросу override superclass property setter привел к этому вопросу StackOverflow:

Переопределение методов получения и установки наследуемых свойств в Python

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...