检测super()是否具有装饰器功能?

时间:2012-02-24 15:16:22

标签: python python-3.x decorator multiple-inheritance

我正在定义几个用于多重继承的类,例如:

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)

但是,我有多个类(例如可能BaconASpam,但不是Eggs)关注何时属性bacon改变了。他们不需要修改值,只需知道新值是什么,比如事件。由于我已经设置了多重继承性质,这意味着必须通知超类有关更改(如果它关心)。

我知道如果我将类名传递给方法装饰器,或者我使用类装饰器,我可能会这样。我不想拥有所有直接的自我类引用,不得不在每个类上面创建大量的装饰器,或者强制方法是相同的名称,因为这些都不是非常pythonic。

我希望得到类似这样的语法:

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

我不关心bacon的先前值,因此如果在设置bacon之后调用此参数,则不需要培根参数。

  • 是否可以检查超类是否有方法 装饰?

  • 如果这不可行,是否有传递事件的替代方法 这个,通过多继承链?

修改

Spam中函数的实际调用将在A中完成,使用@property@bacon.setter,因为这将是初始化bacon的最上层类。一旦知道在self上调用什么函数,问题就在于将调用传播到MI链。

编辑2:

如果我用@bacon.setter覆盖该属性,是否可以确定super()类是否具有bacon的setter?

2 个答案:

答案 0 :(得分:2)

你所要求的可能很适合更完整的信号框架,等等 - 甚至可能会邀请面向预期的编程。

然而,如果没有深入研究它,一个元类和一个装饰者可以做你想要的 - 我想出了这些,我希望它们适合你。

如果您希望将其发展为强大且可用的东西,请写信给我 - 如果不存在这样的情况,那么将实用程序包保存在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元类语法)

编辑 - 关于正在做什么的一些话 这是发生的事情: 元类选择标有“on_close”装饰器的所有方法,然后在类的字典中注册 - 该字典名为_watched_attrs,可以作为普通的类属性进行访问。

元类所做的另一件事是在创建clas后覆盖它的__setattr__方法。这个新的__setattr__只是设置属性,然后检查_wacthed_attrs字典,如果在修改了刚刚更改的属性时注册了该类的任何方法,则调用它。

围绕watcher_setattr的额外间接级别(这是成为每个类的__setattr__的函数),以便您可以在继承链上的每个类上注册要监视的不同属性 - 所有classess有独立的_watched_attrs字典。如果不是这样的话,只有继承链_watched_attrs上最具特色的类才会得到尊重。

答案 1 :(得分:0)

您正在寻找python 属性

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

在Google上搜索override superclass property setter导致此StackOverflow问题:

Overriding inherited properties’ getters and setters in Python