通过方法装饰器修改类属性

3

我有一个类,长这样:

class Test(object):
    data = {}

    @add_to_data(1)
    def method_1(self, x, y):
        pass

    @add_to_data(1, 2, 3)
    def method_2(self, x, y):
        pass

    @add_to_data(5, 6, 7)
    def method_3(self, x, y):
        pass

我希望在“导入时”类的 data 属性为:

>>> Test.data
{'method_1': [1], 'method_2': [1, 2, 3], 'method_3': [5, 6, 7]}

目前我有这个(data 是一个 defaultdict):

def add_to_data(*items):
    def decorator(func):
        for item in items:
            name = func.__name__
            cls = ??
            cls.data[name].append(item)
        return func
    return decorator

但我不知道如何获取类对象。能帮忙吗?

2个回答

4

这种方法不太简洁,但是您可以使用sys._getframe()来检查调用范围(装饰器在类定义上下文中被调用,在那里data是一个局部变量。

因此,以下内容应该可以正常工作:

import sys

def add_to_data(*items):
    def decorator(func):
        name = func.__name__
        data = sys._getframe(1).f_locals.get('data')
        if data is not None:
            data.setdefault(name, []).extend(items)
        return func
    return decorator

如果您不想依赖 sys._getframe 或者想要更明确的方法,您可以使用一个适当的函数属性来标记方法,然后使用类装饰器从所有标记的方法中构建 data 字典,这样就可以简单地实现您的 @add_to_data 函数装饰器。


我没有想到sys._getframe(),你是对的,那个会起作用。 - abarnert
2
我第一次看到这个技巧是在阅读 zope.interface 源代码时。这是一种使用装饰器看起来非常简洁的技巧,但对于其他人来说,实现可能会很困难。 - James Henstridge

1

你不能这样做,因为类对象还不存在。

你可能可以通过创建一个基类,其中包含一个@classmethod作为子类中的装饰器,或者一个将东西存储在其他地方的装饰器,以及一个类装饰器或元类,将存储复制到类的data属性中。

实际上,如果不知道你的实际目标是什么,以及你是在Py2还是Py3(以及你是否只关心CPython还是任何Python),很难知道该提出什么建议。


请参阅https://dev59.com/OHA75IYBdhLWcg3w4tR7。 - mueslo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接