在Python中的哪里存储常量

时间:2018-08-27 09:31:29

标签: python global

我目前正在从事一个项目,在该项目中,我有很多全局常数要硬编码在我的代码中。该项目是用Python编写的。众所周知,访问全局变量时,Python的性能会大大下降。

我可以将那些仅在一种方法中使用的常量移动到使用它们的方法的本地范围内,但这会降低可读性。然后,我仍然会有一些用于多种方法的全局变量,我真的不能将它们隐藏在一个函数的范围之内。

有什么解决方案?我看到一个人在做游戏(Ludum Dare 31),而3:30中的fe可以看到他只有一个大文件constants.py,其中有地狱全局变量(没有{{1 }}关键字)。这是一个好习惯吗?

2 个答案:

答案 0 :(得分:1)

  

众所周知,访问全局变量时,Python的性能会大大下降。

不是“显着”-本地查找确实便宜一些,但是定义局部变量也有代价,因此,除非您在非常紧密的循环中查找全局变量,否则您发现差异的机会确实很小,然后您始终可以在本地为全局别名,例如:

FOO = 42

def foo():
    for i in range(100000000):
        x = i * FOO

FOO = 42

def foo():
    localfoo = FOO
    for i in range(100000000):
        x = i * localfoo

换句话说,在性能是一个真正的问题并且探查器将该全局查询确定为主要瓶颈(这确实非常不可能)之前,您真的不应该担心此处的性能问题,即使如此,我仍然严重怀疑最终,您将获得任何显着的提升-如果对于您的应用程序而言,全局查找的成本已经过高,那么Python不是正确的工具,现在该用C重写此部分了。

  

我可以将那些只在一种方法中使用的常量移动到使用它们的方法的本地范围内,但这会降低可读性。

而且,如上所述,不一定会提高性能:

>>> import dis
>>> import timeit
>>> 
>>> FOO = 42
>>> def foo1():
...     return FOO
... 
>>> def foo3():
...     foo = 42
...     return foo
... 
>>> dis.dis(foo1)
  2           0 LOAD_GLOBAL              0 (FOO)
              3 RETURN_VALUE        
>>>
>>> dis.dis(foo3
... )
  2           0 LOAD_CONST               1 (42)
              3 STORE_FAST               0 (foo)

  3           6 LOAD_FAST                0 (foo)
              9 RETURN_VALUE  
>>> timeit.timeit("func()", "from __main__ import foo1 as func")
0.06334185600280762
>>> timeit.timeit("func()", "from __main__ import foo3 as func")
0.06805109977722168
  

然后,我仍将具有一些在多种方法中使用的全局变量,我真的不能将它们隐藏在仅一个函数的范围内。有什么解决方案?

实际上是什么问题?

  

我看到一个人在做游戏(...),您可以看到他只有一个大文件constants.py,其中有hellta lotta全局变量(没有global关键字)。

在模块顶层定义的所有名称(通过赋值,导入,函数定义或类定义)都是模块的“全局”名称(这是您在Python中可以找到的唯一一种“全局”名称) -没有“应用程序范围内的全局变量”)。 global关键字仅在函数内使用,并且仅当您实际上要在函数内分配给该全局变量时才使用-我们都知道我们不应该这样做,对吗?

  

这是一个好习惯吗?

取决于使用这些“常量”的方式和位置。如果您有多个模块使用的常量,并且这些模块之间没有其他依赖关系,那么这确实有意义,但是大多数时间常量要么仅由一个模块使用,要么其他模块使用它们同一模块中的名称(函数,类等)。

长话短说:常量没什么特别的,它们只是引用对象的名称(您可能没有意识到,但是您所有的函数和类也都是“常量”),因此您只想应用与其他任何事情:您的模块应具有强大的凝聚力(一个模块中的所有内容都密切相关)且耦合度低(您的模块依赖于尽可能少的其他模块)。从这个角度来看,在一个文件中定义10个其他无关模块所依赖的10个无关常量完全是错误的-破坏了内聚力并引入了强耦合。

请注意,您可能还有其他原因要以这种方式“集中”常量(至少其中一些):使配置更容易-但这仅适用于您要使其可配置的常量(将值设为“ pi” “可配置的?),这是一个完全不同的问题。

答案 1 :(得分:-1)

如果您关心的只是在全局名称空间查找中执行代码的性能,则可能可以

globals()['your_constant_name'] # inside your function/method

将直接在全局名称空间中查找内容。请注意,如果由于某种原因该常数不存在,则会引发“ KeyError”而不是“ AttributeError”。

此外,根据Python文档

  

这始终是当前模块的字典(在函数或方法内部,这是定义它的模块,而不是从中调用它的模块)

所以请小心使用。

更新:

这是一个极端的情况(在任何现实情况中都不太可能发生),但是,尽管堆栈结构很大,如@brunodesthuilliers提到的那样,但是如果堆栈框架很大,字典查找的确会提高性能。测试代码:

import itertools,timeit
globals().update({''.join(n):i for i,n in enumerate(itertools.permutations('ABCDEFGHI'))})


def with_dict():
    def func():
        try:
            func()
        except RecursionError:
            globals()['ABCDEFGHI']


def without_dict():
    def func():
        try:
            func()
        except RecursionError:
            ABCDEFGHI

print(timeit.timeit(with_dict)) # output: 0.33404375400277786
print(timeit.timeit(without_dict)) # output: 0.3390919269877486

尽管根据python wiki,字典查找的平均时间复杂度为O(1)