使用类作为装饰器

时间:2018-07-24 17:16:28

标签: python python-3.x class python-3.6 python-decorators

这是我第一次尝试,所以...

我正在关注: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]}

decorator类将用作一种自动的 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不足

1 个答案:

答案 0 :(得分:1)

我认为您在这里缺少了一些不同的基本概念。


首先,当您print一个对象,例如print(f)时,将以__str__类型调用f方法。如果没有人定义__str__,则会调用默认的object.__str__,它只会返回f.__repr__

因此,如果您想更改执行print(f)时发生的情况,则必须f的类型输入__str__(或{ {1}})方法可以满足您的需求。您在其他任何地方所做的任何事情都不会产生任何效果。


初始化__repr__的实例时,不会调用装饰器的__init__;当 class 初始化时,它将被调用。 (或者,当装饰器在类创建后立即在类上被调用)是Foo发生在您使用它创建实例时。

此外,__call__不会从任何地方获取额外的__call__参数,它只会像其他任何普通方法一样获取cls。这就是为什么我们必须首先将修饰的类存储为self的原因。


装饰器的要点是返回一个可以像装饰对象一样使用的对象。您的self.cls可以像类一样被调用-但是这样做时它什么也不返回,而不是返回实例。这意味着不可能创建实例。如果尝试,您只会得到ClassDecorator。这就是None打印print(f)的原因。


最后,转发参数通常同时需要None*args**kwargs将所有意外的位置参数收集到一个元组中,*args将所有意外的关键字参数收集到一个dict中。有人致电**kwargs时,有两个位置参数,没有关键字参数。因此,Foo(10, [1, 2, 3, 4, 5])将为args,而(10, [1, 2, 3, 4, 5])将为kwargs。如果您没有{},那么您会得到*args,因为您没有任何显式的position-or-keyword参数来匹配这些位置参数。


因此,这是一个解决所有问题的示例。我将通过修饰修饰的类来完成此操作,但是请记住,这不是唯一的方法,并且每种方法都有其优缺点。

首先,我将展示如何编写一个装饰器,该装饰器仅对实例变量为TypeErrorx的类执行您想要的操作。

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__


那么,如果我们想在 any 类上使用它而又不事先知道其属性是什么呢?我们可以使用__str__模块来找到属性:

inspect