1

我发布了一个包含 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'
4

2 回答 2

2

你可以想想你的说法

from shared import app

相当于

import shared
app = shared.app

在这里,您有两个不同的变量。改变一个不会改变另一个。当导入发生时,app设置为None。即使shared.app改变了值,app这个模块中的变量也不会改变它的值。当您调用时app.isRunning,应用程序仍然存在None并且您会收到错误消息。

第一种情况按您的预期工作,因为您总是在访问shared.app.

于 2013-09-25T07:17:44.197 回答
2

Python 模块是对象,模块中的名称是该模块的属性。导入(从同一路径)时,模块实例“存储”在sys.modules. 在第一种情况下,两者mainrenderer共享对同一模块实例的引用,因此当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)
于 2013-09-25T07:23:40.100 回答