split()的静态字符串是运行时还是编译时操作?

Ter*_*non 5 python

来自Perl背景,我习惯于:

my @words = qw(fee fi fo fum);
Run Code Online (Sandbox Code Playgroud)

我想知道以下Python是否会导致运行时性能下降:

words = 'fee fi fo fum'.split()
Run Code Online (Sandbox Code Playgroud)

或者如果words在编译时绑定.

ars*_*jii 5

您可以使用dis模块查看字节码:

>>> from dis import dis
>>>
>>> def f():
...     words = 'fee fi fo fum'.split()
... 
>>> dis(f)
  2           0 LOAD_CONST               1 ('fee fi fo fum')
              3 LOAD_ATTR                0 (split)
              6 CALL_FUNCTION            0
              9 STORE_FAST               0 (words)
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE 
Run Code Online (Sandbox Code Playgroud)

split(作为加载方式LOAD_ATTR)被调用via CALL_FUNCTION,其结果在运行时分配给wordsvia STORE_FAST.换句话说,x.split()即使x是硬编码字符串,也不会在编译时解析.

当然,使用'fee fi fo fum'.split()和它的结果之间的性能差异,即['fee', 'fi', 'fo', 'fum']可以忽略不计,所以不要仅仅为了提高性能而从一个改变到另一个.

  • 如果它是在编译时处理的,你只会看到一个加载列表或元组的`LOAD_CONST`,然后是一个`STORE_FAST`来将它存储在变量中. (4认同)
  • @EMS你可以看到`split`被调用是因为`CALL_FUNCTION`操作数(它调用`split`由`LOAD_ATTR`加载).我在答案中详细阐述了一下. (3认同)

650*_*502 3

即使在 PERL 中,它也不能在编译时绑定,因为每次执行该语句时都必须创建一个新的列表。

换句话说,在 Python 中,当你看到

x = ['fee', 'fi', 'fo', 'fum']
Run Code Online (Sandbox Code Playgroud)

代码不能只加载特定的对象地址,x因为每次进入语句时它都必须是一个新的列表对象。考虑

def foo():
    return ['fee', 'fi', 'fo', 'fum']

x = foo()
x[0] = 'bar'

print foo()[0]   # must be 'fee'
print x[0]       # must be 'bar'
Run Code Online (Sandbox Code Playgroud)

然而,Python 对不可变对象做了一些优化;例如:

def foo():
    return (1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

实际上总是返回相同的对象(它不会每次都分配一个新的元组),并且对于

def foo():
    return 3 * 4
Run Code Online (Sandbox Code Playgroud)

它实际上返回 12,而没有在运行时进行乘法。

但是,您的示例无法解析为加载在编译时计算的常量,它必须至少是一个列表“文字”(这实际上是每次评估时创建一个新对象的列表生成器)。

IIRC 常量折叠仅在编译时针对数字/字符串上的二元运算符完成,而不是针对任何函数调用(请注意,在 Python 中,更改标准函数等操作是合法的,即使是不好的做法len,因此您不能确定len("foo")始终返回3)。

对仅传递常量的标准字符串的方法调用在 IMO 中确实可以在编译时移动,但据我所知,目前尚未完成(而且这并不常见,因此可能不值得付出努力)。