python - 使用类作为装饰器
问题描述
这是我第一次尝试这个,所以...
我正在关注: https ://www.python-course.eu/python3_decorators.php
目标是当我像这样初始化 Foo 的实例时:
f = Foo(10, [1, 2, 3, 4, 5])
print(f) 的输出需要如下所示:
Instance of Foo, vars = {'x': 10, 'y': [1, 2, 3, 4, 5]}
装饰器类的作用是作为一种自动repr。
我有这个:
class ClassDecorator(object):
def __init__(self, cls):
self.cls = cls
print("An instance of Foo was initialized")
def __call__(self, cls, *args, **kwargs):
print("Instance of Foo, vars = ", kwargs)
@ClassDecorator
class Foo(object):
def __init__(self, x, y):
self.x = x
self.y = y
f = Foo(10, [1, 2, 3, 4, 5])
print(f)
产生:
An instance of Foo was initialized
Instance of Foo, vars = {}
None
我不认为我想要 *args,但没有它我的程序会出错:
Traceback (most recent call last):
An instance of Foo was initialized
File "C:/Users/Mark/PycharmProjects/main/main.py", line 17, in <module>
f = Foo(10, [1, 2, 3, 4, 5])
TypeError: __call__() takes 2 positional arguments but 3 were given
我理解缺少参数的问题,但是如果 10 是 'x' 而 [1, 2, 3, 4, 5] 是 'y',那么我就是不明白为什么 **kwargs 是不够的。
解决方案
我认为您在这里遗漏了一些不同的基本概念。
首先,当你print
是一个对象时,比如print(f)
,它所做的就是调用__str__
类型的方法f
。如果没有人定义__str__
,则调用默认值object.__str__
,它只返回f.__repr__
。
所以,如果你想改变你做 a 时发生的事情print(f)
,你必须给f
' 类型一个__str__
(或__repr__
)你想要的方法。您在其他任何地方所做的任何事情都不会产生任何影响。
初始化__init__
实例时不会调用您的装饰器;它在类被初始化Foo
时被调用。(或者,更确切地说,当装饰器在类上被调用时,在类创建之后)当你使用它来创建一个实例时就会发生这种情况。__call__
此外,这__call__
不会从任何地方获得额外的cls
参数,它只会得到self
,就像任何其他正常方法一样。这就是为什么我们必须像一开始一样存储装饰类self.cls
。
装饰器的重点是返回一个可以像装饰对象一样使用的对象。你ClassDecorator
可以像一个类一样被调用——但是当你这样做时它什么也不返回,而不是返回一个实例。这意味着不可能创建实例;如果你尝试,你就会得到None
。这就是为什么print(f)
打印None
.
最后,转发参数通常需要*args
和**kwargs
。*args
将任何意外的位置参数收集到一个元组中,并将**kwargs
任何意外的关键字参数收集到一个字典中。当有人调用Foo(10, [1, 2, 3, 4, 5])
时,有两个位置参数,没有关键字参数。args
将会如此,(10, [1, 2, 3, 4, 5])
而且kwargs
将会如此{}
。如果你没有 a *args
,那么你会得到 a TypeError
,因为你没有任何显式的位置或关键字参数来匹配这些位置参数。
因此,这是一个解决所有这些问题的示例。我将通过修改装饰类来做到这一点,但请记住,这不是唯一的方法,每种方法都有优缺点。
首先,我将展示如何编写一个装饰器,它只对实例变量为x
and的类执行您想要的操作y
。
class ClassDecorator(object):
def __init__(self, cls):
def _str(self):
typ = type(self).__name__
vars = {'x': self.x, 'y': self.y}
return(f"Instance of {typ}, vars = {vars}")
cls.__str__ = _str
self.cls = cls
print(f"The class {cls.__name__} was created")
def __call__(self, *args, **kwargs):
print(f"An instance of {self.cls.__name__} was created with args {args} and kwargs {kwargs}")
return self.cls(*args, **kwargs)
现在,当您使用它时,它会执行您想要的操作:
>>> @ClassDecorator
... class Foo(object):
... def __init__(self, x, y):
... self.x = x
... self.y = y
The class Foo was created
>>> f = Foo(10, [1, 2, 3, 4, 5])
An instance of Foo was created with args (10, 20) and kwargs {}
>>> print(f)
Instance of Foo, vars = {'x': 10, 'y': 20}
>>> f
<__main__.Foo at 0x127ecdcf8>
当然,如果你想覆盖最后一件事,你想定义__repr__
与__str__
.
那么,如果我们想在任何类上使用它,而不事先知道它的属性是什么?我们可以使用该inspect
模块来查找属性:
def _str(self):
typ = type(self).__name__
vars = {name: value for name, value in inspect.getmembers(self) if not name.startswith('_')}
return(f"Instance of {typ}, vars = {vars}")
推荐阅读
- android - 自定义键盘的滑动输入
- javascript - 学习 JS - 无法将计数器的结果用于字段
- java - ConcurrentHashMap 上的同步块:锁定将放在什么位置?
- angular - 将类添加到动态创建的组件角度
- android - 图像未显示在 Xamarin Forms Android 项目中
- sql - SQL 循环数月
- java - 是否可以在 mongo db 中使用 Java 将输入作为流插入记录?
- android - Volley 无法访问应用程序中的 URL,Android 10
- css - 滚动不显示整个项目列表
- reactjs - 从项目中删除导入/无周期警告