我发布了一个包含3个小文件程序的紧凑示例,我想了解为什么更改几行会有所不同。
# main.py
from renderer import Renderer
import shared
class Application():
def __init__(self):
self.isRunning = True
self.renderer = Renderer()
self.globalize()
def globalize(self):
shared.app = self
def main():
app = Application()
while ( app.isRunning ):
app.renderer.quit()
print "program end"
if __name__ == "__main__":
main()
# shared.py
app = None
# renderer.py
import shared
class Renderer():
def __init__(self):
pass
def quit(self):
shared.app.isRunning = False
现在,文件shared.py
的这种用法为Renderer
类提供了Application
类的访问权限,该类具有Renderer
的实例作为成员,用于任何恶魔我想到的程序设计。我的问题是,当renderer.py
更改时,不再保证此访问权限的原因如下:
# renderer.py -- ( new )
from shared import app
class Renderer():
def __init__(self):
pass
def quit(self):
app.isRunning = False
原始renderer.py
使程序结束,后来renderer.py
引发异常,为什么会这样?
renderer.py", line 7, in quit
app.isRunning = False
AttributeError: 'NoneType' object has no attribute 'isRunning'
答案 0 :(得分:2)
你可以想到你的陈述
from shared import app
等同于
import shared
app = shared.app
在这里,您有两个不同的变量。改变一个不会改变另一个。导入发生时,app
设置为None
。即使shared.app
更改了值,此模块中的app
变量也不会更改其值。当您致电app.isRunning
时,应用仍然是None
,您就会收到错误消息。
第一种情况正如您所期望的那样,因为您始终访问shared.app
。
答案 1 :(得分:2)
Python模块是对象,模块中的名称是此模块的属性。导入(来自相同路径)模块实例“存储”在sys.modules
中。在第一种情况下,main
和renderer
共享对同一模块实例的引用,因此当main
重新绑定shared.app
时,renderer
中也会显示这一点。在第二种情况下,您使app
成为本地(模块本地)名称,因此重新绑定shared.app
不会影响renderer.app
绑定的内容。这里重要的一点是,Python的“变量”与C变量的共同点很少。后者是内存地址的符号名称,而在Python中,它们只是键 - >值对,属于命名空间的键(名称)(模块命名空间,类命名空间,函数的本地命名空间)。所以
from module import something
行只是:
的快捷方式import module
# create a local name 'something'
something = module.something
del module
# at this point both module.something and something are bound to the
# same object, but they are distinct names in distinct namespaces
# rebinds local name 'something'
# now `module.something` and `something` point to dffererent objects
something = object()
FWIW你的设计不是“恶魔般的”,它只是EvilGlobals。授予Renderer
实例app
访问权限的简单明了的方法是将app
传递给渲染器:
class Renderer(object):
def __init__(self, app):
self.app = app
class Application(object):
def __init__(self):
self.renderer = Renderer(self)