列表和元祖的使用优化

python3 -m timeit 'x=(1,2,3,4,5,6)'
20000000 loops, best of 5: 9.97 nsec per loop
python3 -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 50.1 nsec per loop

两三个元素,使用 tuple,元素多一点使用 namedtuple。

# option A
empty_list = list()
# option B
empty_list = []

区别主要在于 list() 是一个 function call,Python 的 function call 会创建 stack,并且进行一系列参数检查的操作,比较 expensive,反观 [] 是一个内置的 C 函数,可以直接被调用,因此效率高。

>>> dis.dis(lambda : dict())
1 0 LOAD_GLOBAL 0 (dict)
3 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
6 RETURN_VALUE
>>> dis.dis(lambda : {})
1 0 BUILD_MAP 0
3 RETURN_VALUE
>>> %timeit dict()
133 ns ± 2.95 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit {}
74.6 ns ± 3.07 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

字符串拼接优化

Python 字符串直接进行 + 操作,是 O(n) 复杂度,n 为字符串长度。但是下面几个方法可以更快。

# method 1
s = ''
for n in range(0, 100000):
    s += str(n)
    
# method 2
l = []
for n in range(0, 100000):
    l.append(str(n))
 
s = ' '.join(l)
 
# method 3
s = " ".join(map(str, range(0, 10000)))

方式三性能最优。其次,数目少的时候,+= 快,数目非常大的时候,join 更快。

for 一行语句

attributes = ['name', 'dob', 'gender']
values = [
	['jason', '2000-01-01', 'male'], 
	['mike', '1999-01-01', 'male'],
	['nancy', '2001-02-01', 'female']
]
 
# expected output:
[{'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'}, 
{'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'}, 
{'name': 'nancy', 'dob': '2001-02-01', 'gender': 'female'}]
 
# 一行语句
[dict(zip(attributes, value)) for value in values]

函数

回顾了 global 和 nonlocal 和闭包函数。

  • global 可以在函数内部修改全局变量
  • nonlocal 在嵌套函数中声明一个非局部变量,可以在内部函数中访问和修改外部函数的变量。

函数式编程

关于 map()、filter() 和 reduce()三个函数,需要注意的是:

  1. map()在 Python 2.x 返回的是一个列表;而在 Python 3.x 中返回一个 map 类,可以看成是一个迭代器。
  2. filter()在 Python 2.x 中返回的是过滤后的列表, 而在 Python 3.x 中返回的是一个 filter 类,可以看成是一个迭代器,有惰性运算的特性, 相对 Python2.x 提升了性能, 可以节约内存。
  3. reduce() 函数在 Python3 中已经被从全局名字空间里移除了,它现在被放置在 functools 模块里,如果想要使用它,则需要通过引入 functools 模块来调用 reduce() 函数。
numbers = [1, 2, 3, 4, 5]
squared_numbers = map(lambda x: x**2, numbers)
print(list(squared_numbers))
 
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers))
 
from functools import reduce
 
numbers = [1, 2, 3, 4, 5]
sum_of_numbers = reduce(lambda x, y: x + y, numbers)
print(sum_of_numbers)

成员变量的访问性

  • _xxx 单下划线,只有类对象和子类对象自己能访问到这些变量,需通过类提供的接口进行访问;
  • __xxx 类中的私有变量/方法名,只有类对象自己能访问,连子类对象也不能访问到这个数据。

如果是私有变量,可以强行访问,但是不推荐。Python 没有完全限制访问私有。

class A:
    __a = 1
 
class B(A):
    pass
 
b = B()
print(b._A__a)

Python 常量缓存

Python 内部会对 -5 到 256 的整型维持一个数组,起到一个缓存的作用。这样,每次你试图创建一个 -5 到 256 范围内的整型数字时,Python 都会从这个数组中返回相对应的引用,而不是重新开辟一块新的内存空间。

'==' 操作符却不同,执行 a == b 相当于是去执行 a.__eq__(b)

assert

在Python中,assert 语句用于调试目的,它允许你检查程序中的条件,并在条件为 False 时触发异常(默认是 AssertionError)。如果你希望在运行Python程序时忽略所有的 assert 语句,可以使用 Python 的 -O(优化)标志来启动解释器。当使用 -O 标志时,Python 会忽略所有的 assert 语句,并且还会生成 .pyo 文件(优化后的字节码文件),而不是标准的 .pyc 文件。

以下是如何使用 -O 标志的例子:

python -O your_script.py

或者,如果你使用的是 Python 3.x 版本,命令可能会这样写:

python3 -O your_script.py

当你以优化模式运行脚本时,所有的 assert 语句将被当作无操作(no-op)处理,即它们不会被执行,也不会产生任何效果。

需要注意的是,忽略 assert 语句应该仅限于生产环境中,因为 assert 是一种有用的调试工具,在开发和测试阶段不应该被忽略。assert 语句可以帮助开发者快速找到代码中的逻辑错误,确保程序按照预期工作。在发布或部署应用程序之前,你应该移除或禁用所有不应用于生产的断言。

此外,如果需要更细粒度地控制哪些 assert 语句生效或忽略,可能需要重构代码,使用条件语句或其他机制来替代 assert,并根据配置或环境变量来决定是否执行这些检查。

dis 函数

dis 函数查看字节码。