结论
在底层实现中,在声明python类时,会按照声明的顺序把变量和函数的指针装到某个容器里,并在调用的时候从头到尾遍历,名字和用法匹配即返回。
事发经过
之前在写python的时候,脑子一抽,写了这样的代码(大致)
classchar_embedding():def__init__(self, size_1, size_2):
self.char_embedding = nn.Embedding(size_1, size_2)defchar_embedding(self, x):return self.char_embedding(x)
很有趣的一点是这些东西也能跑起来。但是在想用对象调用这个类的函数的时候,总传出一些奇怪的错误,比如给forword函数传参不对啦之类的。
实验验证 & 结论
经过试验我发现,python的类在储存变量以及函数的时候,应该有一个特定的容器按照定义顺序存下他们的指针。我们可以用下面这个简单的例子做演示:
classA():def__init__(self):
self.A = nn.Embedding(1,1)defA(self, x):return x
这种情况和我上述代码出现了相同的错误,在通过A的实例调用函数A时,实际调用的是
nn.Embedding
这个‘A’。但反之,如果我们换一下变量和函数的定义顺序:
classA():def__init__(self)->None:
self.nothing_important =1defA(self, x):
self.A = nn.Embedding(1,1)return x
这种情况下,类内函数A先于类内变量A声明,在想通过实例调用变量A的时候实际得到的是函数A。
有一点需要说明,这个
nn.Embedding
实际上是一个类的实例,其继承自
module
类,有一个需要(且已经)复写的
forward
函数,通过调用
nn.functional.Embedding
这个函数进行的实现。换言之,其使用方法和函数时一样的,用小括号进行传参,所以会产生混淆。如果变量是’真正意义上uncallable的一个东西,那是一定能通过有小括号与否区分开的。
那么综上几点,我们可以推测,底层类的实现应该有一个容器,装着按照声明顺序填充的变量、函数的指针,在类的实例试图调用的时候,会在这个容器中按照从头到尾的顺序进行遍历,遇到名字匹配且用法兼容的项就返回相应的指针(函数)或内容(变量)。
版权归原作者 Petersburg 所有, 如有侵权,请联系我们删除。