Python中导入类函数中的全局变量

4 投票
2 回答
3846 浏览
提问于 2025-04-17 19:00

我正在尝试把我的代码分散到不同的文件中,以提高可读性,但在导入的文件中遇到了未定义的全局变量的问题。有没有比在调用函数时故意传递所有必要值更好的解决方案?

我现在的代码是这样的:

#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

还是说有更好的方法?

顺便说一下,上面的代码是高度抽象的。实际代码涉及很多类和函数,传递了许多不同类型的变量,包括一些大型字典。

提前谢谢你们!

2 个回答

2

如果 aBar 的一个特征或设置,那么它应该是 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
5

如果你只是想知道为什么它不工作:

首先,main函数并没有设置一个全局的a,而是一个局部的。如果你想让它成为全局的,你需要明确说明:

def main():
    global a
    a = 1
    # ...

但是这并不能解决问题,因为在Python中“全局”是针对每个模块的。换句话说,这只是创建了一个foo.a,而你在bar.Bar中的代码会寻找bar.a,但这个并不存在。

如果你想的话,可以import foo,然后访问foo.a。(在这种情况下,循环依赖应该不是问题,if __name__ == '__main__'也不会被执行两次。)或者你可以假设foo已经被导入,然后使用sys.modules['foo'].a。或者你可以从foo中将a注入到bar的字典里。还有很多其他的技巧。但这些都是不太好的做法。

如果你无法让全局变量工作,正确的做法几乎总是不要使用全局变量。(事实上,即使你可以让全局变量工作,通常这也是正确的选择。)

那么,怎么做呢?在不知道更多细节的情况下,很难猜测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']的值(这是一个不可变的浮点数;你不能修改它),但你确实用一个不同的值替换了它(因为configs是一个可变的dict;你可以随意修改它)。如果身份不明确,你可以尝试在不同的地方打印出id(self.configs)id(self.configs['gravity'])等信息。

撰写回答