我想从装饰器中更新一个“类范围”列表,它装饰类的方法,并将每个修饰的方法添加到该列表中。
这就是我所想到的:
def add(meth: callable):
Spam.eggs.append(func)
return meth
class Spam:
eggs = []
@add
def meth(self):
pass但是,这是行不通的,因为Spam还没有完成在到达@add时定义自己的工作,因此add会引发一个NameError,正如注释中指出的那样。
我还尝试了一个类方法:
class Spam:
eggs = []
@classmethod
def add(cls, meth: callable):
cls.eggs.append(meth)
return meth
@add
def meth(self):
pass但是这也不起作用,因为当到达@add时,add被绑定到classmethod修饰的实例,这个实例是不可调用的。
我需要这个是为了:
我有一个类,它有几个使用一个参数(除了self)的方法来转换该对象,这些方法可以相互组合。我想用这样的方式来装饰它们,使它们自动添加到类中的列表中。
例如:
from typing import List
def transform_meth(meth: callable):
TextProcessor.transforms.add(meth)
return meth
class TextProcessor:
transforms: List[callable] = []
@transform_meth
def m1(self, text):
return text
@transform_meth
def m2(self, text):
return text
def transform(self, text):
for transform in self.transforms:
text = transform(text)
return text我可以手动在列表中添加方法,但是我发现装饰器更清晰,因为它接近于方法的定义,因此在定义新方法时要比手动将它添加到列表中更容易记住。
发布于 2019-07-10 18:38:26
您的当前方法失败了,因为当transform_meth被调用时,TextProcessor还没有绑定到任何东西(或者如果是,则在class语句完成时该对象会被覆盖)。
简单的解决方案是在transform_meth语句中定义class,这样它就可以简单地将transforms声明为一个非局部变量。但是,这是行不通的,因为class语句没有建立新的作用域。
相反,您可以定义一个创建装饰符的函数,该函数接受所需的列表(此时仅是class语句正文中的一个名称,而不是任何假定范围中的名称)。该函数在list参数上返回一个闭包,以便您可以附加到它。
def make_decorator(lst):
# *This* will be the function bound to the name 'transform_meth'
def _(meth):
lst.append(meth)
return meth
return _
class TextProcessor:
transforms: List[callable] = []
transform_meth = make_decorator(transforms)
@transform_meth
def m1(self, text):
return text
@transform_meth
def m2(self, text):
return text
def transform(self, text):
for transform in self.transforms:
text = transform(text)
return text
del transform_meth # Not needed anymore, don't create a class attribute发布于 2019-07-10 18:40:28
由于每个方法的arg都是self,所以可以将其附加到对象实例,如下所示:
from functools import wraps
def appender(f):
@wraps(f)
def func(*args, **kwargs):
if f not in args[0].transforms:
args[0].transforms.append(f)
return f(*args, **kwargs)
return func
class Foo(object):
def __init__(self):
self.transforms = []
@appender
def m1(self, arg1):
return arg1
@appender
def m2(self, arg1):
return arg1
def transform(self, text):
methods = [f for f in dir(self) if not f.startswith("__") and callable(getattr(self,f)) and f != 'transform']
for f in methods:
text = getattr(self,f)(text)
return text
f = Foo()
f.transform('your text here')
print(f.transforms)输出:
[<function Foo.m1 at 0x1171e4e18>, <function Foo.m2 at 0x1171e4268>]https://stackoverflow.com/questions/56976329
复制相似问题