如何使用装饰器将 class 的方法添加到 class 内的列表
How to add methods of class to a list inside the class with a decorator
我想从装饰 class 方法并将每个装饰方法添加到该列表的装饰器中更新 "class-wide" 列表。
这是我想到的:
def add(meth: callable):
Spam.eggs.append(func)
return meth
class Spam:
eggs = []
@add
def meth(self):
pass
虽然这不会起作用,因为当达到 @add
时 Spam
还没有完成自我定义,因此 add
引发 NameError
,正如所指出的在评论中。
我也试过一个class方法:
class Spam:
eggs = []
@classmethod
def add(cls, meth: callable):
cls.eggs.append(meth)
return meth
@add
def meth(self):
pass
但这也不起作用,因为当达到 @add
时,add
绑定到 classmethod
装饰实例,该实例不可调用。
这是我需要的:
我有一个 class 有几个方法,这些方法采用一个参数(除了 self
之外),这些方法以这些方法可以相互组合的方式转换该对象。我想以自动将它们添加到 class.
中的列表的方式装饰它们中的每一个
例如:
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
我可以手动添加列表中的方法,但我发现装饰器更清晰,因为它接近于方法的定义,因此在定义新方法时更容易记住装饰它而不是手动将其添加到列表中。
您当前的方法失败了,因为当调用 transform_meth
时,TextProcessor
尚未绑定到任何对象(或者如果已绑定,则该对象在 class
语句完成时被覆盖).
简单的解决方案是在 class
语句中定义 transform_meth
,这样它就可以简单地将 transforms
声明为非局部变量。但是,这不会起作用,因为 class
语句不会建立新的范围。
相反,您可以定义一个 创建 装饰器的函数,它采用所需的列表(此时只是 class
主体中的一个名称声明,而不是来自任何假定的范围)。该函数 returns 对列表参数的闭包
以便您可以附加到它。
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
由于每个方法的 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>]
我想从装饰 class 方法并将每个装饰方法添加到该列表的装饰器中更新 "class-wide" 列表。
这是我想到的:
def add(meth: callable):
Spam.eggs.append(func)
return meth
class Spam:
eggs = []
@add
def meth(self):
pass
虽然这不会起作用,因为当达到 @add
时 Spam
还没有完成自我定义,因此 add
引发 NameError
,正如所指出的在评论中。
我也试过一个class方法:
class Spam:
eggs = []
@classmethod
def add(cls, meth: callable):
cls.eggs.append(meth)
return meth
@add
def meth(self):
pass
但这也不起作用,因为当达到 @add
时,add
绑定到 classmethod
装饰实例,该实例不可调用。
这是我需要的:
我有一个 class 有几个方法,这些方法采用一个参数(除了 self
之外),这些方法以这些方法可以相互组合的方式转换该对象。我想以自动将它们添加到 class.
例如:
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
我可以手动添加列表中的方法,但我发现装饰器更清晰,因为它接近于方法的定义,因此在定义新方法时更容易记住装饰它而不是手动将其添加到列表中。
您当前的方法失败了,因为当调用 transform_meth
时,TextProcessor
尚未绑定到任何对象(或者如果已绑定,则该对象在 class
语句完成时被覆盖).
简单的解决方案是在 class
语句中定义 transform_meth
,这样它就可以简单地将 transforms
声明为非局部变量。但是,这不会起作用,因为 class
语句不会建立新的范围。
相反,您可以定义一个 创建 装饰器的函数,它采用所需的列表(此时只是 class
主体中的一个名称声明,而不是来自任何假定的范围)。该函数 returns 对列表参数的闭包
以便您可以附加到它。
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
由于每个方法的 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>]