如何使用方法在Python中初始化模块级变量?

时间:2018-02-25 23:38:29

标签: python scope

在Java中,如果我想要“一个可用于类中所有方法的不变的变量而不作为参数传递”,我会这样做:

public class MyClass {
  private static final OtherClass OTHER_CLASS = buildOtherClass()

  ...

  private static OtherClass buildOtherClass() {
    //<creation logic>
  }

}

但是,如果我尝试在Python脚本中做类似的事情,我会收到错误:

$ cat python_test.py
#!/usr/bin/env python3

DICTIONARY = buildDictionary()

def methodThatUsesDictionary()
  ...

def buildDictionary():
  d = {}
  for i in range(5):
    d[str(i)] = i
  return d

if __name__ == '__main__':
  methodThatUsesDictionary()

$ ./python_test.py
Traceback (most recent call last):
  File "./python_test.py", line 3, in <module>
    DICTIONARY = buildDictionary()
NameError: name 'buildDictionary' is not defined

如果我在方法声明下面移动变量声明,代码将按预期运行。但是,对我而言,感觉真的不合理,因为依赖于元素排序的代码合法性, - 文件中的方法存在,我不明白为什么它不能仅仅因为它“没有”而被找到已被宣布尚未“。 Moverover,将一般(非特定于方法的)变量放在文件的顶部是一个代码约定,我发现它非常明智和有用 - “这里是我们将要处理的事情的概述”(虽然我猜测在Python中不那么真实,你没有类型声明来提供额外的上下文。)

我可以看到几种可以解决这个问题的方法,其中没有一种方法在美学方面令人愉悦:

  • 如上所述,我可以在变量之前声明方法。
  • 我可以将构造函数方法放在子模块中,然后将其导入到脚本中 - 我不喜欢这个,因为提取逻辑可能只有几行,这似乎太过分了。
  • 我可以将此脚本的逻辑放在Python类中,并在__init__方法中初始化该变量。再次,这似乎是不必要的开销。
  • 将变量设置为从__main__方法调用的方法的全局变量,如下面的代码所示。我看到很多指导意见表明全局变量是危险信号,所以我怀疑我也应该避免这种情况。

(我需要在这里放置文字,否则下面的片段是未格式化的,大概是因为紧接在上面的子弹)

$ cat python_test_2.py
#!/usr/bin/env python3

def initializeGlobalVariables():
  global DICTIONARY
  DICTIONARY = {}
  for i in range(5):
    DICTIONARY[str(i)] = i

def doOtherLogic():
  for key in DICTIONARY:
    print('DICTIONARY[' + str(key) + '] is ' + str(DICTIONARY[key]))

def main():
  initializeGlobalVariables()
  doOtherLogic()

if __name__ == '__main__':
  main()
$ ./python_test_2.py
DICTIONARY[0] is 0
DICTIONARY[1] is 1
DICTIONARY[2] is 2
DICTIONARY[3] is 3
DICTIONARY[4] is 4

可能我正在尝试做的事情完全是单声道的,因此缺乏支持是“按设计”。我希望从更有经验的Python用户那里得到一些关于风格和结构的反馈。

1 个答案:

答案 0 :(得分:1)

Python和Java基本上是非常不同的语言,Python更像是脚本语言而不是编译(尽管它可以即时编译)。此外,Python并没有真正具有&#34;一个不变的变量......&#34;的概念,至少不是变量名。变量名实际上是指向任何类型数据的指针(请参阅:动态类型),如果没有一些特殊的python装饰器,您可以随时更改变量的值,有时甚至会对您造成损害。

更重要的是,由于其脚本性质(并且可以交互式执行),事情的顺序很重要:

>>> def test():
...     print x
... 
>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test
NameError: global name 'x' is not defined
>>> x = 1
>>> test()
1
>>> 

在这种情况下,函数x中的全局test只是一个指针,x尚不存在并不重要因为你没有& #39; t试图运行test。当来自强类型和编译语言时,这似乎是违反直觉的,但是它允许很多很好的东西,比如动态导入只会在使用函数时加载模块:

>>> def dyn_import():
...     import random
...     for i in xrange(5):
...             print random.random()
... 
>>> random.random()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined
>>> dyn_import()
0.246957404578
0.302236192705
0.614960539467
0.0928052533036
0.389804554563
>>> random.random()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined

如果在函数内部使用之前需要完全定义每个变量,那么定义dyn_import将失败,除非加载random并且了解random.random()的含义。在这种情况下,在实际执行该功能之前不会加载它。此外,由于该函数不会将模块放入脚本的全局命名空间,因此只要函数完成就会丢弃该模块。

这只是一个非常有益的例子,虽然这是一个非常重要的例子。