代码之家  ›  专栏  ›  技术社区  ›  MarkS

将类用作修饰器

  •  0
  • MarkS  · 技术社区  · 7 年前

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

    我在跟踪: https://www.python-course.eu/python3_decorators.php

    目标是,当我像这样初始化foo的实例时:

    f = Foo(10, [1, 2, 3, 4, 5])
    

    打印输出(f)需要如下所示:

    Instance of Foo, vars = {'x': 10, 'y': [1, 2, 3, 4, 5]}
    

    decorator类是作为一种自动的 再PR .

    我有这个:

    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
    

    我想我不需要*参数,但如果没有它,我的程序会出错:

    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 回复  |  直到 7 年前
        1
  •  1
  •   Keyur Potdar    7 年前

    我认为你遗漏了一些不同的基本概念。


    首先,当你 print 一个物体,比如 print(f) ,这就是所谓的 __str__ 方法的类型 f . 如果没有人定义 _战略__ ,默认值 object.__str__ 被调用,它只是返回 f.__repr__ .

    所以,如果你想改变当你做一个 打印(F) 不得不 f 的A型 _战略__ (或) __repr__ )实现您所需功能的方法。你在别的地方做的任何事都不会有任何效果。


    你的装修工 __init__ 当的实例 Foo 初始化;当 初始化。(或者,更确切地说,当修饰符在类上被调用时,就在类创建之后)它是 __call__ 当您使用它来创建实例时就会发生这种情况。

    还有,那 _呼叫__ 不会有额外的 cls 从任何地方得到的参数 self 像其他普通方法一样。这就是为什么我们要把装饰课作为 self.cls 首先。


    装饰器的作用是返回一个可以像装饰对象一样使用的对象。你的 ClassDecorator 可以像类一样调用,但这样做时它不会返回任何内容,而不会返回实例。这意味着不可能创建实例;如果您尝试,您只需 None . 这就是为什么 打印(F) 印刷品 没有 .


    最后,转发参数通常需要 *args **kwargs . *精氨酸 将任何意外的位置参数收集到元组中,并且 **夸格斯 将任何意外的关键字参数收集到dict中。当有人调用 Foo(10, [1, 2, 3, 4, 5]) ,有两个位置参数,没有关键字参数。所以 args (10, [1, 2, 3, 4, 5]) kwargs {} . 如果你没有 *精氨酸 然后你会得到一个 TypeError ,因为您没有任何显式的位置参数或关键字参数来匹配这些位置参数。


    所以,这里有一个修复所有这些问题的例子。我将通过修改修饰类来完成它,但请记住,这不是唯一的方法,而且每种方法都有优缺点。

    首先,我将演示如何编写一个只满足实例变量为 x 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>
    

    当然,如果你想重写最后一件事,你要定义 _报告__ _战略__ .


    那么,如果我们想用这个 任何 类,而不预先知道它的属性是什么?我们可以使用 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}")