我正在尝试将我的代码分散到不同的文件中以提高可读性,而且我遇到了在导入文件中引用未定义的全局名称的问题。有没有比在调用时故意将所有必要值传递给函数更好的解决方案?
我的代码目前是这样的:
#foo.py
import bar
def main():
a = 1
b = bar.Bar()
while True:
a += 1
b.incr()
print a
print b.c
if __name__ == '__main__':
main()
#END foo.py
-
#bar.py
class Bar:
def __init__(self):
self.c = 0
def incr(self):
self.c += a
#END bar.py
它给了我NameError:未定义全局名称'a'。我是否需要像这样重写main():
def main():
b = new bar.Bar()
while True:
a += 1
b.incr(a)
print a
print b.c
并像这样重写incr():
def incr(self,a):
c += a
或者有更好的方法吗?
为了记录,上面的代码被大量抽象。实际的代码涉及很多类和函数,它们传递了许多类型的变量,包括一些大的字典。
提前致谢!
答案 0 :(得分:4)
如果你只是想知道为什么它不起作用:
首先,main
未设置全局a
,而是设置本地def main():
global a
a = 1
# ...
。如果你想让它全球化,你必须明确:
foo.a
但这不会解决问题,因为Python中的“全局”是每个模块。换句话说,这只会创建一个bar.Bar
,但bar.a
中的代码会查找import foo
,但不存在。
如果您愿意,可以foo.a
然后访问if __name__ == '__main__'
。 (在这种情况下,循环依赖不应该是一个问题,并且foo
将不会执行两次。)或者您可以假设已导入sys.modules['foo'].a
,并使用a
}。或者您可以将bar
注入foo
的{{1}}字典中。或许多其他技巧。但这些都是可怕的事情。
如果你不能做全球工作,那么正确的答案几乎总是不使用全球性的。 (事实上,即使你可以进行全球工作,这通常也是正确答案。)
那么,你是怎么做到的?在不了解更多详细信息的情况下,很难猜测a
是否属于显式模块全局,类属性,实例属性,incr
的参数等。但要弄清楚哪一个是考虑到a
代表什么,并且这样做最有意义。
在评论中,您建议您拥有一堆这些配置变量。如果你将它们全部包装在dict
中并将dict
传递给每个对象的构造函数,那将使事情变得更简单。
更重要的是,dict
是对可变对象的引用。复制它只是对同一个对象的另一个引用。
main.py:
def main():
configs = {}
configs['gravity'] = 1.0
rock = rps.Rock(configs)
rock.do_stuff()
configs['gravity'] = 2.1
rock.do_stuff()
if __name__ == '__main__':
main()
rps.py:
class Rock(object):
def __init__(self, configs):
self.configs = configs
def do_stuff(self):
print('Falling with gravity {}.'.format(self.configs['gravity']))
当你运行它时,它会打印出如下内容:
Falling with gravity 1.0.
Falling with gravity 2.1.
你没有修改值configs['gravity']
(这是一个不可变的浮点数;你不能修改它),但你有用不同的值替换它(因为{{1是一个可变的configs
;你可以修改它就好了)。如果身份不明确,您可以尝试在各个地方打印dict
,id(self.configs)
等内容。
答案 1 :(得分:2)
如果a
是Bar
的特征或配置,则它应该是其实例或类属性。
class Bar(object):
class_step = 1
def __init__(self, inst_step):
self.inst_step = inst_step
self.c = 0
self.step_mode = 'class'
def inc(self):
self.c += Bar.class_step if self.step_mode == 'class' else self.inst_step