代码之家  ›  专栏  ›  技术社区  ›  Lokesh Agrawal

通过重写_new__来装饰类方法不起作用?

  •  1
  • Lokesh Agrawal  · 技术社区  · 7 年前

    装饰师:

    def debug(func):
        msg = func.__name__
    
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(msg)
            return func(*args, **kwargs)
        return wrapper 
    
    def debugmethods(cls):
        for key, val in vars(cls).items():
            if callable(val):
                setattr(cls, key, debug(val))
        return cls
    

    现在我想装饰我班上的所有方法。一种简单的方法是在类的顶部使用@debugmethods注释,但我试图了解另外两种不同的方法。

    a) 压倒 __new__

    class Spam:
        def __new__(cls, *args, **kwargs):
            clsobj = super().__new__(cls)
            clsobj = debugmethods(clsobj)
            return clsobj
    
        def __init__(self):
            pass
    
        def foo(self):
            pass
    
        def bar(self):
            pass
    
    spam = Spam()
    spam.foo()
    

    b) 书写元类

    class debugmeta(type):
        def __new__(cls, clsname, bases, clsdict):
            clsobj = super().__new__(cls, clsname, bases, clsdict)
            clsobj = debugmethods(clsobj)
            return clsobj
    
    class Spam(metaclass = debugmeta):     
        def foo(self):
            pass
    
        def bar(self):
            pass
    
    spam = Spam()
    spam.foo()
    

    我不确定

    1. 为什么“a)压倒一切 __新的__ “不行吗?
    2. 为什么选择方法签名 __新的__ 元类是不同的吗?

    有人能帮我理解我在这里错过了什么吗。

    1 回复  |  直到 7 年前
        1
  •  5
  •   Martijn Pieters    7 年前

    你似乎把两者混淆了 __new__ 还有元类。 被调用来创建一个新对象(一个类的实例,一个元类的类),它不是一个“类创建”挂钩。

    • Foo(...) 翻译成 type(Foo).__call__(Foo, ...) 看见 special method lookups 这是为什么。这个 type() 类的定义是它的元类。
    • type.__call__ 在以下情况下使用的实现 Foo 是将调用的自定义Python类 __新的__ __init__ 方法,如果结果确实是 类别:

      def __call__(cls, *args, **kwargs):  # Foo(...) -> cls=Foo
          instance = cls.__new__(cls, *args, **kwargs)  # so Foo.__new__(Foo, ...)
          if isinstance(instance, cls):
              instance.__init__(*args, **kwargs)        # Foo.__init__(instance, ...)
          return instance
      

      Foo.__new__ 仅当 属于 都是创造出来的。

    __新的__ 足以初始化实例的属性。要不是 不变的 int tuple ,您只能使用 __新的__ 准备新的实例状态,因为一旦创建了不可变对象,就不能更改其属性。 __新的__ 当您想要更改什么类型的实例时,它也很有用 ClassObj() 生成(例如创建单例或生成专门的子类)。

    相同的 __call__ __新的__ 也许 这个过程适用于元类。A. class Foo: ... class Spam(metaclass = debugmeta): ... ,就是说 debugmeta('Spam', (), {...}) 被称为,意思是 debugmeta.__new__(debugmeta, 'Spam', (), {...}) 被称为。

    你的第一次尝试 A. 背景 Spam.__new__ 不起作用,因为你是 不创建类对象 那里相反 super().__new__(cls) 创建一个空的 Spam() 例子 vars() 返回一个空字典,然后 debugmethods() 结果什么也没做。