繁体   English   中英

在Python中的哪里存储常量

[英]Where to store constants in Python

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

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

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

众所周知,在访问全局变量时,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关键字仅在函数内使用,并且仅当您实际上要在函数内分配给该global时才使用-我们都知道我们不应该这样做,对吗?

这是一个好习惯吗?

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

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

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

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

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM