如何使用Python3.4的枚举而不显着减速?

2024-05-15 13:14:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我在写一个tic-tac-toe游戏,并使用一个枚举来表示三个结果——losedraw,和{}。我认为它比使用字符串("lose", "win", "draw")来表示这些值要好。但是使用枚举给了我一个显著的性能打击。在

这里是一个最小的例子,我只引用Result.lose或文本字符串lose。在

import enum
import timeit
class Result(enum.Enum):
    lose = -1
    draw = 0
    win = 1

>>> timeit.timeit('Result.lose', 'from __main__ import Result')
1.705788521998329
>>> timeit.timeit('"lose"', 'from __main__ import Result')
0.024598151998361573

这比简单地引用全局变量慢得多。在

^{pr2}$

我的问题是:

  • 我知道在Python中全局查找比本地查找慢得多。但为什么枚举查找更糟呢?在
  • 如何在不牺牲性能的情况下有效地使用枚举?事实证明,枚举查找完全控制了我的tic-tac-toe程序的运行时。我们可以在每个函数中保存枚举的本地副本,或者将所有内容包装在一个类中,但这两种方法似乎都很尴尬。在

Tags: 字符串fromimport游戏mainenumresulttic
1条回答
网友
1楼 · 发布于 2024-05-15 13:14:13

你在计时循环。字符串文字本身被完全忽略:

>>> import dis
>>> def f(): "lose"
... 
>>> dis.dis(f)
  1           0 LOAD_CONST               1 (None)
              3 RETURN_VALUE        

这是一个什么也不做的函数。所以计时循环需要0.024598151998361573秒来运行一百万次。在

在本例中,字符串实际上变成了f函数的docstring:

^{pr2}$

但是,如果没有赋值或者表达式的一部分,CPython通常会省略代码中的字符串:

>>> def f():
...     1 + 1
...     "win"
... 
>>> dis.dis(f)
  2           0 LOAD_CONST               2 (2)
              3 POP_TOP             

  3           4 LOAD_CONST               0 (None)
              7 RETURN_VALUE        

这里的1 + 1被折叠成一个常量(2),而字符串文本又一次消失了。在

因此,不能将其与查找enum对象上的属性相比较。是的,查找属性需要周期。但查找另一个变量也是如此。如果您真的担心性能,您可以始终缓存属性查找:

>>> import timeit
>>> import enum
>>> class Result(enum.Enum):
...     lose = -1
...     draw = 0
...     win = 1
... 
>>> timeit.timeit('outcome = Result.lose', 'from __main__ import Result')
1.2259576459764503
>>> timeit.timeit('outcome = lose', 'from __main__ import Result; lose = Result.lose')
0.024848614004440606

timeit测试中,所有变量都是局部变量,因此Result和{}都是局部查找。在

enum属性查找比“常规”属性查找花费的时间稍长:

>>> class Foo: bar = 'baz'
... 
>>> timeit.timeit('outcome = Foo.bar', 'from __main__ import Foo')
0.04182224802207202

这是因为enum元类包含一个{a1},每次查找属性时都会调用它;enum类的属性是在专门的字典中查找的,而不是在__dict__类中查找。执行该钩子方法和附加属性查找(访问映射)都需要额外的时间:

>>> timeit.timeit('outcome = Result._member_map_["lose"]', 'from __main__ import Result')
0.25198313599685207
>>> timeit.timeit('outcome = map["lose"]', 'from __main__ import Result; map = Result._member_map_')
0.14024519600206986

在一个井字游戏中,你一般不会担心什么是微不足道的时间差异。当人类玩家的速度比你的电脑慢几个数量级时就不会了。人类玩家不会注意到1.2微秒和0.024微秒之间的差别。在

相关问题 更多 >