我正在学习 Lutz 的 python。我遇到了一个代码示例,其中的逻辑,直到最后我都无法理解。代码示例:
def Tracer(classname, supers, classdict):
print('Tracer')
aClass = type(classname, supers, classdict)
class Wrapper:
print('Wrapper')
def __init__(self, *args, **kargs):
print('__init__ Wrapper', args)
self.wrapped = aClass(*args, **kargs)
def __getattr__(self, attrname):
print('Trace', attrname)
return getattr(self.wrapped, attrname)
return Wrapper
class Person(metaclass=Tracer):
print('Person')
def __init__(self, name, hours, rate):
print('__init__ Person')
self.name = name
self.hours = hours
self.rate = rate
def pay(self):
return self.hours * self.rate
bob = Person('Bob', 40, 50)
#print()
#print(bob.wrapped.name)
显然,直到 Person 类被分配实例(通过 command bob = Person('Bob', 40, 50)
)的那一刻,执行线程首先进入 Person 并执行 command print('Person')
,之后 Tracer(...) 函数在 Person 底部隐式执行类,它是一个元类并返回对 Wrapper 类的引用(并执行命令print('Wrapper')
),即 作为执行 Tracer(...) 的结果,我们在 Person 类的底部获得了对 Wrapper 类的隐式引用。
现在我不明白。
为什么,在命令执行后bob = Person('Bob', 40, 50)
,我们运行构造函数Wrapper.__init__(...)
,而不是Person.__init__(...)
?如果构造函数位于 Person 类中高于对 Wrapper 的隐式引用(位于 Person 类的最底部),我无法理解它如何Wrapper.__init__(...)
拦截参数('Bob', 40, 50)Person.__init__(...)
从上到下读取代码?
一步步。
Tracer 函数属于模块的命名空间。
执行已达到 Person 类的定义,正在执行其代码。每个 def 用方法名称完成属性字典。如果类中没有代码,除了定义方法,那么顺序无关紧要。
作为第 2 步的结果,我们得到名称“Person”和一组属性。而不是构造类,运行时将此操作委托给元类(无论指定为元类)
元类(在本例中为 Tracer 函数)
type()
根据传递的数据(名称、属性...)构造 Person 类,还定义了 Wrapper 类,其中使用了这个 Person 类。同时,它返回 Wrapper 类,该类属于 Person 名称下的模块命名空间。也就是说,从现在开始,在 Person 名称下,我们实际上有 Wrapper 类,因此
Person('Bob', 40, 50)
创建 Wrapper 类的实例并调用Wrapper.__init__
,它写入控制台,然后在自身内部创建类的实例Person
并调用Person.__init__