1

为什么我问这个问题,因为我总是担心这种风格的代码

def callsomething(x):
    if x in (3,4,5,6):
        #do something

如果函数 callsomething 被频繁调用,(3,4,5,6) 是否浪费了太多的空间和时间?在 C 之类的某些语言中,它可能会像常量一样放入数据段中,但在 python 中,我不会知道它是如何工作的,所以我倾向于编写这样的代码

checktypes = (3,4,5,6)#cache it
def callsomething(x):
    global checktypes
    if x in checktypes:
        #do something

但是经过测试我发现这种方式会使程序变慢,在更复杂的情况下,代码会是这样的:

types = (3,4,5,6)
def callsomething(x):
    global types
    for t in types:
        t += x
        #do something

仍然比这慢

def callsomething(x):
    for t in (3+x,4+x,5+x,6+x):
        #do something

在这种情况下,程序必须创建 (3+x,4+x,5+x,6+x),对吗?但它仍然比第一个版本快,不过不会太多。

我知道 python 中的全局 var 访问会减慢程序的速度,但它与创建结构相比如何?

4

2 回答 2

8

别担心,它被存储为一个常量(这解释了为什么它比你预期的要快)

>>> def callsomething(x):
...     if x in (3,4,5,6): pass
... 
>>> import dis
>>> dis.dis(callsomething)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               5 ((3, 4, 5, 6))
              6 COMPARE_OP               6 (in)
              9 POP_JUMP_IF_FALSE       15
             12 JUMP_FORWARD             0 (to 15)
        >>   15 LOAD_CONST               0 (None)
             18 RETURN_VALUE        

x在 a 中查找set应该更快,对吧?但是哦哦...

>>> def callsomething(x):
...     if x in {3,4,5,6}: pass
... 
>>> dis.dis(callsomething)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (3)
              6 LOAD_CONST               2 (4)
              9 LOAD_CONST               3 (5)
             12 LOAD_CONST               4 (6)
             15 BUILD_SET                4
             18 COMPARE_OP               6 (in)
             21 POP_JUMP_IF_FALSE       27
             24 JUMP_FORWARD             0 (to 27)
        >>   27 LOAD_CONST               0 (None)
             30 RETURN_VALUE      

set 是可变的,所以 Python 直到最近才进行这种优化。Python3.3 认为把它做成frozenset 是安全的

Python 3.3.0 (default, Sep 29 2012, 17:17:45) 
[GCC 4.7.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def callsomething(x):
...     if x in {3,4,5,6}: pass
... 
>>> import dis
>>> dis.dis(callsomething)
  2           0 LOAD_FAST                0 (x) 
              3 LOAD_CONST               5 (frozenset({3, 4, 5, 6})) 
              6 COMPARE_OP               6 (in) 
              9 POP_JUMP_IF_FALSE       15 
             12 JUMP_FORWARD             0 (to 15) 
        >>   15 LOAD_CONST               0 (None) 
             18 RETURN_VALUE         
>>> 
于 2013-06-12T03:46:36.973 回答
1

如果你真的想做一个 hack,这样你就可以获得元组以外的东西的常量,你可以做一些像

def foo(x, dont_use_this=frozenset([1,2,3])):
    if x in dont_use_this:
        # do something
    return x

但不要那样做。

于 2013-06-12T04:07:43.403 回答