装饰类 - 装饰一次忘掉它?
我成功创建了一个装饰器,可以给任何类型的类添加一个标准接口,这样就能更方便地访问和整合它们。
我一直在避免使用 metaclass(元类),因为相关资料说这通常是过于复杂的解决方案,大多数情况下可以用类装饰器来替代。不过,我有一个困惑:
def Decorator(somearg):
def wrapper(cls):
clsinit = cls.__init__
cls.members = []
def __init__(self, *args, **kwargs):
#do something with somearg...
self.__class__.members.append(self)
clsinit(self,*args,**kwargs)
cls.__init__ = clsinit
return cls
return wrapper
@Decorator('thearg')
class A(object):
pass
a = A()
b = A()
在使用 Python 调试器时,当我导入时,类 A 会立即被装饰器用参数 'thearg' 装饰。但是每次我创建 A() 的实例时,这个实例直接调用了装饰器中定义的初始化方法,而没有经过之前的层。这很好,因为我希望我的类能记录每个成员,而不是每次创建新实例时都重置。但我不太明白这是为什么。
有没有人能解释一下在这种情况下 Python 解释器的工作原理?
3 个回答
感谢 @JBernardo 和 @martineau 的回答。我写这段代码的目的是想让类的对象像容器一样,方便我更轻松地访问它们的实例。不过,我希望一次只访问每个类的实例,而不是在同一个继承的类中访问所有实例。
其实再次研究代码让我明白了我的问题:
a) 装饰器被调用时带上了一个参数 somearg。
b) 下一行的内容(类 A 的定义)被用作包装函数的参数。在这个函数里,我做了以下几件事:
1) 记住最初的类初始化方法,以避免无限递归。
2) 定义一个新的初始化方法,以便在实例级别进行修改。
3) 为这个类绑定一个新的初始化方法。
4) 返回一个经过转换的类对象。
c) 由于返回的是一个类对象,每次我创建一个实例时,都会直接进入初始化方法,因为这是在新类中绑定的方式。
如果你只是修改一个类,而不是创建一个新的类,那就不需要用到一个包装函数(其实应该是包装类):
def Decorator(cls):
clsinit = cls.__init__
cls.members = []
def __init__(self, *args, **kwargs):
clsinit(self, *args, **kwargs)
self.__class__.members.append(self)
cls.__init__ = __init__
return cls
顺便说一下,你应该创建一个基础类,然后从这个基础类继承:
class Base(object):
members = []
def __init__(self):
self.__class__.members.append(self)
class A(Base):
pass
这样做会更简洁。
好的,关于你更新问题中提到的修正代码,你问:
“但是每次我创建 A() 的实例时,这个实例直接调用了装饰器中定义的 init,而没有经过之前的层。”
之所以这样,是因为当wrapper()
这个类装饰器函数(它是由Decorator()
函数创建的)应用到class A
时,它会把A
的__init__()
替换成它自己动态定义的wrapper.__init__()
函数。这个函数就是每当创建A
的实例时被调用的,它的写法是先做一些事情,然后最后再调用原来的A.__init__()
。