9

Stack Overflow 有很多关于 python 中的全局变量的问题,这似乎给来自其他语言的人带来了一些困惑。范围界定规则并不像许多其他背景的人所期望的那样工作。

同时,代码的组织不是在类级别上,而是在模块级别上。因此,当所有东西都不一定包含在类中时,本来可以在成员变量中找到的状态可以放在模块级变量中。

所以我的问题是两部分:

1)我是否应该避免使用全局变量(特别是在函数中设置它们并使用 global 关键字)?

2) 如果#1 是肯定的,是否有预期使用它们的常见模式?

我在一个有很多不同语言的地方工作,我想减少混乱并确保 pythonistas 以后不会讨厌我。

感谢您的任何建设性意见。

4

4 回答 4

7

我强烈建议您阅读这篇题为Singletons and their Problems in Python 的博文。它让我重新思考我对全局变量的使用。一些选择引用:

但要小心。仅仅因为你没有实现单例设计模式并不意味着你避免了单例的核心问题。单例的核心问题是全局共享状态。单例只不过是一个美化的全局变量,在像 Java 这样的语言中,有很多原因可以让你使用像单例这样的东西。在 Python 中,我们为单例提供了一些不同的东西,它有一个非常纯真的名字,隐藏了血腥的细节:模块。

没错,Python 模块是单例的。它与单例模式有相同的问题,只是它有点糟糕。

以下是具有此类共享状态可能导致的问题的一个示例:

为了不谈论无关紧要的事情,让我们看一下标准库中的一个模块,mimetypes 模块。

看一看:

inited = False

def init(files=None):
    global inited
    db = MimeTypes()
    ...

这是来自 Python 附带的 mimetypes 模块的实际代码,只是删除了更多血腥的细节。关键是,有共享状态。共享状态是一个布尔标志,如果模块已初始化,则为 True,否则为 False。现在这种特殊情况可能不是那么有问题(相信我,确实如此),因为 mimetypes 会自行初始化,但您可以看到 init 函数有一个 files 参数。如果将文件列表传递给该函数,它将使用来自这些文件的 mime 信息重新初始化内存中的 mime 数据库。现在想象一下,如果你有两个库用两个不同的源初始化 mimetypes 会发生什么……</p>

这是一个足够常见的模式,我自己已经完成了......但例如更好的方法是:init返回实现所有方法的类的实例,并且代码的其他部分可以init获取具有不干扰前者的不同参数的不同实例。“缺点”是您必须将此实例传递给任何不想初始化新实例的代码,但“缺点”的优点是它使您的依赖关系变得显而易见。

无论如何,简而言之,我会尽量避免它,但如果你对具有隐式单例的代码感到满意,那就去吧。

于 2012-09-28T15:24:51.060 回答
3

我对你的另一个问题发表了评论,所以这是我的 2 美分:

Python 是面向对象的,但您不必使用它。但是,如果您选择这样做,您可以利用 Class 的这种机制来保存一些属性:

class Config():
"""
hold some vars for example.
"""
def __init__(self):
    self.CONFIG_LOG_FILE_DIR='/tmp2/ozn/venus_mon_log/'
    self.DATE_FORMAT="%Y%m%d"
    #self.FILE_NAME_BASE_TEMPLATE=eval('datetime.datetime.now().strftime(self.DATE_FORMAT)')+'-venus_utilization.log'
    self.FILE_NAME_BASE_TEMPLATE='venus_utilization.log'
    self.FILE_NAME=self.CONFIG_LOG_FILE_DIR+self.FILE_NAME_BASE_TEMPLATE
    self.MAX_FILE_SIZE=1024*1024*50 # in Byte, 50 in MB.

您可以创建一个实例,让您访问属性:

   cfg = Config()

然后使用以下命令访问它们:

   cfg.MAX_FILE_SIZE

甚至改变它们:

   cfg.MAX_FILE_SIZE=50000 
   cfg.MAX_FILE_SIZE=calculateNewSize() 

等等...您还可以执行以下操作:

 # this will print all items that an instance has    
 if options.debug:
    print "DEBUG INFO:"
    for k,v in vars(cfg).iteritems():
        print k,v
于 2012-09-28T15:28:39.067 回答
3

简而言之,是的,您应该避免使用global关键字。它可能在语言中是有原因的,但我通常认为它是一种代码味道——如果你有一些想要保持的状态,请将它封装在一个类中。这远没有使用global.

于 2012-09-28T15:17:33.760 回答
2

全局变量并不是真正的禁忌,只是你必须记住在使用它们之前在你的函数中将它们声明为全局变量。在我看来,这实际上使它们更加清晰,因为用户看到您明确使用了全局变量。

我会更害怕非 pythonistas 不理解 python 全局变量,修改你的代码并且不添加正确的全局声明。

于 2012-09-28T15:10:15.497 回答