【问题标题】:Detect if super() has a function with decorator?检测 super() 是否有装饰器的功能?
【发布时间】:2012-03-15 01:01:50
【问题描述】:

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

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 之后调用它,则不需要 bacon 参数。

  • 是否可以检查一个超类是否有这个方法 装饰师?

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

编辑:

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

编辑 2:

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

【问题讨论】:

  • 显然 OP 的意思是“{Detect if super() has a function} with a decorator”,而不是“Detect if super() has a {function with a decorator}”
  • None 默认值主要用于当您实际上必须运行一些感兴趣的东西以获得默认值时。在这里,您可以只使用def foo(self, bacon=100, **kwargs): self.bacon = bacon,但如果您尝试支持在任意 kwargs 周围洗牌以真正取悦 OOP 小伙伴,则应该使用 def __init__(self, **kwargs): self.bacon = kwargs.get('bacon', 100)
  • @MikeGraham 培根参数可以从任何传递的kwargs 中删除,因为我认为默认的object 不会对此感到满意。目的是在到达object 时,每个参数都从kwargs 中删除。
  • 我忘记了新版本的 Python object.__init__ 不像以前那样接受任意参数。

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


【解决方案1】:

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

但是,无需深入研究,元类和装饰器就可以满足您的要求 - 我想出了这些,希望它们对您有用。

如果您想将其发展为强大且可用的东西,请写信给我 - 如果不存在这样的东西,那么值得为此在 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,可以作为普通类属性访问。

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

围绕watcher_setattr 的额外间接级别(这是成为每个类的__setattr__ 的函数,以便您可以在继承链上的每个类上注册不同的属性以进行监视-所有类都可以独立访问@ 987654329@ 字典。如果不是这样,只会尊重继承链上最特殊的类_watched_attrs

【讨论】:

  • 在我接受之前需要做更多的调查,所有这些元、装饰器和递归的东西现在让我超负荷。 :P
  • 那里没有“递归”:-)。实际上只是看起来很复杂-我会写一些解释性文字。
  • 无限递归是一个单独的问题,是我在看到您的帖子之前尝试考虑的另一种方法,哈哈。非常感谢您的解释性文字,我认为这将回答我的问题(尽管我可能会将其简化为仅支持单个属性)。
  • 其他存在并提供类似功能的东西:Decovent
【解决方案2】:

你正在寻找python 属性

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

在 google 上搜索 override superclass property setter 会出现这个 StackOverflow 问题:

Overriding inherited properties’ getters and setters in Python

【讨论】:

  • 你指的是this answer吗?有没有办法作为装饰器来做这件事,或者有办法避免直接引用父级?
  • 我认为它不适用于SpamBacon 有setter,但Eggs 没有,因为Eggs 不直接继承自Bacon
猜你喜欢
  • 1970-01-01
  • 2017-11-19
  • 2011-07-26
  • 2017-05-13
  • 2017-10-30
  • 2014-04-24
  • 2021-04-06
  • 2022-01-09
  • 2020-12-10
相关资源
最近更新 更多