Python 内部原理

来自百合仙子's Wiki
跳转到导航 跳转到搜索

寻找全局变量

在函数定义时,函数的代码对象被初始化一个 co_names 属性(即 func.__code__.co_names ,保存了函数中全局变量的名字。函数另有 __globals__ 属性指向 globals() 所返回的那个字典。和 globals() 函数一样,这是函数定义的地方的全局变量表,而非被调用的地方者。因此,在 exec() 中调用的函数并不会使用在 exec() 调用时传递进去的全局变量表。[1]

在函数中含有 exec 调用时,解释器将使用 LOAD_NAME 指令取代 LOAD_GLOBAL 指令,以寻找在 exec 中生成的局部变量。

性能

字符串处理

连接多个字符串, str.join 效率比直接相加略高(在没有编译期优化的情况下)(Python 3.3):

In [1]: a = 'a'

In [2]: %timeit a + 'b' + 'c' + 'd'
1000000 loops, best of 3: 371 ns per loop

In [3]: %timeit '%s%s%s%s' % (a, 'b', 'c', 'd')
1000000 loops, best of 3: 506 ns per loop

In [4]: %timeit ''.join((a, 'b', 'c', 'd'))
1000000 loops, best of 3: 348 ns per loop

正则

在进行大量项目的正则替换时,如果匹配项较少,先做子串判断可以更快(Python 3.3):

import re
pkgrel_re = re.compile(r'^\s*pkgrel=(\d+)')

def plus_one(m):
  s = m.group()
  return s[:m.start(1)] + str(int(m.group(1))+1) + s[m.end(1):]

def a(l):
  if 'pkgrel' in l:
    l = pkgrel_re.sub(plus_one, l)
  return l

def b(l):
  l = pkgrel_re.sub(plus_one, l)
  return l

In [4]: %timeit a('this is only a test line')
1000000 loops, best of 3: 276 ns per loop

In [5]: %timeit b('this is only a test line')
100000 loops, best of 3: 2.77 µs per loop

In [6]: %timeit a('pkgrel=4 # test')
100000 loops, best of 3: 8.73 µs per loop

In [7]: %timeit b('pkgrel=4 # test')
100000 loops, best of 3: 8.37 µs per loop

PyPy 2.1beta1-2 (Python 3) 版差异较小:

In [16]: %timeit a('this is only a test line')
10000000 loops, best of 3: 21.6 ns per loop

In [17]: %timeit b('this is only a test line')
1000000 loops, best of 3: 618 ns per loop

In [18]: %timeit a('pkgrel=4 # test')
100000 loops, best of 3: 4.16 µs per loop

In [19]: %timeit b('pkgrel=4 # test')
100000 loops, best of 3: 4.19 µs per loop

列表处理

(Python 3 中)向列表末尾添加两个数据时, .append.extend 列表要快,但是比 .extend tuple 慢不少。如果数据多于两个,则 .extend 总是更快:

In [1]: timeit a = [1]; a.append(2); a.append(3)
1000000 loops, best of 3: 457 ns per loop

In [2]: timeit a = [1]; a.extend([2,3])
1000000 loops, best of 3: 470 ns per loop

In [3]: timeit a = [1]; a.extend((2,3))
1000000 loops, best of 3: 328 ns per loop

In [4]: timeit a = [1]; a.append(2); a.append(3); a.append(4)
1000000 loops, best of 3: 576 ns per loop

In [5]: timeit a = [1]; a.extend([2,3,4])
1000000 loops, best of 3: 494 ns per loop

In [6]: timeit a = [1]; a.extend((2,3,4))
1000000 loops, best of 3: 334 ns per loop

JSON 序列化

对于很简单的字典,使用 CPython 3.3.2:

one k-v:
bson:     6.48 µs per loop
json:    15.6  µs per loop
ujson:    1.39 µs per loop
tornado: 16.2  µs per loop
four k-v's:
bson:     7.98 µs per loop
json:    18    µs per loop
ujson:    2.28 µs per loop
tornado: 18.4  µs per loop

空间大小差异不大

编译期优化

多个字面字符串相加、字面数值计算会有优化。如以下计算均有优化(Python 3.3、Python 2.7):

a = 4 * 8
return 5 * 8 * a

但是以下则没有:

return a * 5 * 8

在 Python 3.3 中,观察到以下优化:

>>> import dis
>>> def t():
...   return '%s%s%s%s' % ('a', 'b', 'c', 'd')
...
>>> dis.dis(t)
  2           0 LOAD_CONST               7 ('abcd')
              3 RETURN_VALUE

Python 2.7 中没有这种优化。如果参与格式化的对象中包含变量亦不会优化。

参见

外部链接

工具

  • uncompyle2, A Python 2.5, 2.6, 2.7 byte-code decompiler, written in Python 2.7.

参考资料