Python 当跨模块派生类时,名称解析是如何工作的?
类B和C都派生自基类A,并且都不重写A的方法test()。B与A在同一模块中定义;C在单独的模块中定义。为什么调用B.test()会打印“hello”,而调用C.test()会失败?两个调用都不应该执行.test(),从而能够解析mod1命名空间中的符号“message”吗 我也很感激收到关于这种行为记录在哪里的提示,因为我一直无法找到任何东西。调用C.test()时名称是如何解析的,并且“消息”可以以某种方式注入其中一个名称空间吗 FWIW,我没有使用实例变量(例如set A.message=“hello”)的原因是我想访问一个“全局”单例对象,而不想在每个其他对象中都有一个对它的显式引用 mod1.py:Python 当跨模块派生类时,名称解析是如何工作的?,python,python-2.7,Python,Python 2.7,类B和C都派生自基类A,并且都不重写A的方法test()。B与A在同一模块中定义;C在单独的模块中定义。为什么调用B.test()会打印“hello”,而调用C.test()会失败?两个调用都不应该执行.test(),从而能够解析mod1命名空间中的符号“message”吗 我也很感激收到关于这种行为记录在哪里的提示,因为我一直无法找到任何东西。调用C.test()时名称是如何解析的,并且“消息”可以以某种方式注入其中一个名称空间吗 FWIW,我没有使用实例变量(例如set A.message=
import mod2
class A(object):
def test(self):
print message
class B(A):
pass
if __name__ == "__main__":
message = "hello"
A().test()
B().test()
mod2.C().test()
mod2.py:
import mod1
class C(mod1.A):
pass
输出为:
$ python mod1.py
hello
hello
Traceback (most recent call last):
File "mod1.py", line 14, in <module>
mod2.C().test()
File "mod1.py", line 5, in test
print message
NameError: global name 'message' is not defined
$python mod1.py
你好
你好
回溯(最近一次呼叫最后一次):
文件“mod1.py”,第14行,在
mod2.C().test()
测试中的第5行文件“mod1.py”
打印消息
NameError:未定义全局名称“消息”
非常感谢 是否可以使用类属性而不是全局属性?以下作品
import mod2
class A(object):
message = "Hello" # Class attribute (not duplicated in instances)
def test(self):
print self.message # Class A attribute can be overridden by subclasses
class B(A):
pass
if __name__ == "__main__":
A().test()
B().test()
mod2.C().test()
不使用globals更干净:在上面的代码中,message
被显式地附加到它所使用的类
话虽如此,我也很好奇为什么
mod2.C().test()
找不到全局消息
但是,如果删除了交叉导入(在mod1.py
中没有主程序,也没有importmod2
):从mod3.py
导入mod1
和mod2
,执行mod1.message=“Hello”
和mod2.C().test()
。因此,我想知道这个问题是否与交叉导入无关……EOL是正确的,将程序的“主要”部分移动到新文件mod3.py中确实可以使事情正常进行
进一步澄清了这个问题
在我最初的问题中,原来变量message
存储在\uuuu main\uuuu
模块名称空间中,因为mod1.py是作为脚本运行的。mod2导入mod1,但它获得一个单独的mod1
名称空间,其中变量message
不存在。下面的代码片段在将消息
写入mod1
的名称空间(我不建议在现实生活中这样做)时更清楚地说明了这一点,从而导致了预期的行为
import sys
class A(object):
def test(self):
print message
class B(A):
pass
if __name__ == "__main__":
import mod2
message = "hello"
sys.modules["mod1"].message = message
A().test()
B().test()
mod2.C().test()
我认为现实世界中最好的修复方法是将程序的“主要”部分移动到一个单独的模块中,正如EOL所暗示的,或者:
class A(object):
def test(self):
print message
class B(A):
pass
def main():
global message
message = "hello"
A().test()
B().test()
# resolve circular import by importing in local scope
import mod2
mod2.C().test()
if __name__ == "__main__":
# break into mod1 namespace from __main__ namespace
import mod1
mod1.main()
旁注:(其他)当您这样交叉导入时,问题很容易出现。例如,pythonmod2.py
失败,因为在定义classC
时尚未定义mod1.A
。我的经验是,像这样的交叉导入通常表明某些代码在逻辑上会被移动到其他地方,直到没有交叉导入为止。你说的“我不想在每个其他对象中都有对它的显式引用”是什么意思?我不确定我是否理解使用类属性的问题:a.message=“hello”
设置一个类属性,该类属性不存储在类实例中。@EOL,我现实世界中的“全局”问题是一个数据库句柄。我不希望将其存储在对象/类中,因为有许多对象和类型需要访问数据库。另一个问题是,我通过多处理
队列
s传递这些对象,并且数据库句柄必须是特定于进程的,因此将其添加到对象/类是很麻烦的。最后,目标是基类是用户可扩展的,因此是跨模块的。理想情况下,我希望了解为什么我对名字决议的期望是错误的,并找出今后的方向。我明白了。是的,在这里使用一个全球性的。我不确定用户可扩展性的要求与交叉导入有何关系:我指的是这样一个事实:最好不要使用mod1.py
doimportmod2
(如果mod2.py
doesimportmod1
)。我想知道这个问题是否与这个交叉导入无关。我想我现在已经解决了——请参阅下面的答案并感谢您的帮助。那么,这是否意味着在原始主程序中,A().test()
使消息
在test()
函数中被解释为sys.modules[''''.message
,但是mod2.C().test()
使得message
被解释为mod1.message
?如果是这样的话,我很想知道解释器内部是如何工作的。test()
的字节码只需读取LOAD\u全局消息
:是什么允许解释器在A().test()
和mod2.C().test()
中对其进行不同的解释?@EOL,没错。mod1.py的可用性是sys.modules['''u____']
和sys.modules['mod1']
的两倍。A.test()
有两个实例,每个模块一个。在本例中,A().test()
调用前者,mod2.C().test()
最终调用后者。如果您在python源代码中遵循ceval.c,它将显示LOAD\u GLOBAL
从定义调用函数的任何位置读取全局符号表(这也是有文档记录的)。问题在于,由于\uuuu main\uuuu
命名空间,调用函数A.test()
有两个版本。这就是我的困惑!谢谢,现在一切都清楚了+1.谢谢你的回答。:)