前言
Python
相对于其他编程语言被认为较慢,主要有以下几个原因:
-
解释型语言:
Python
是一种解释型语言,而不是编译型语言。在程序执行时,Python
解释器会逐行解释和执行代码,这会引入一定的开销。相比之下,编译型语言如C++
在运行之前会将代码编译成机器码,因此执行速度更快。
-
动态类型和动态内存管理:
Python
是一种动态类型语言,变量的类型可以在运行时动态改变。这导致在运行时需要进行类型检查和转换,增加了运行时的开销。此外,Python
还使用了自动的内存管理机制,即垃圾回收机制,用于自动分配和释放内存。垃圾回收的开销也会影响Python
的性能。 -
全局解释锁(
GIL
):Python
的标准解释器(CPython
)中存在一个全局解释锁(GIL
),它限制了同一时间只能有一个线程执行Python
字节码。这意味着在多线程的情况下,Python
无法有效地利用多核处理器的优势,导致在处理计算密集型任务时性能受限。但是,值得注意的是,在I/O
密集型任务中,由于GIL
会在I/O
操作时释放,因此多线程可以提高性能。 -
部分库的实现方式:
Python
的一些常用库和模块使用了底层用C
或C++
编写的扩展模块,这些模块的性能比纯Python
代码高。然而,并非所有的库都使用这种方式,一些纯Python
实现的库性能可能较低。
常规小技巧
-
列表推导(List Comprehension):使用列表推导可以将
for
循环转化为更高效的单行表达式。它可以通过在一个列表中使用for
循环来创建另一个列表。列表推导通常比显式的for
循环更快,因为它利用了底层的优化机制。numbers = [1, 2, 3, 4, 5]
squared = [x**2 for x in numbers] -
map() 函数:使用
map()
函数可以将一个函数应用到一个可迭代对象的所有元素上,并返回一个新的可迭代对象。这种方式可以比显式的for
循环更快。numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers)) -
使用
numpy
库:如果你在处理大量的数值数据,使用numpy
库可以显著加快for
循环的运算。numpy
是一个高性能的科学计算库,提供了多维数组和向量化操作。import numpy as np
numbers = np.array([1, 2, 3, 4, 5])
squared = numbers**2 -
并行计算:如果你需要对一个较大的数据集进行计算,并且这些计算是相互独立的,你可以考虑使用并行计算来加速
for
循环。Python
中有一些库,如multiprocessing
和concurrent.futures
,可以方便地实现并行计算。from multiprocessing import Pool
def square(x):
return x**2
numbers = [1, 2, 3, 4, 5]
with Pool() as pool:
squared = pool.map(square, numbers)
Pandas加速循环
-
Iterrows()
为每一行返回一个Series
,因此它以索引对的形式遍历DataFrame
,以Series
的形式遍历目标列,这使得它比标准循环更快。
import pandas as pd |
-
apply
本身并不快,但与DataFrame
结合使用时,它具有很大的优势。这取决于apply
表达式的内容。 如果它可以在Cython
中执行,那么apply
要快得多。
# 创建示例DataFrame |
-
利用向量化
Pandas Vectorization
的优势来创建真正高效的代码
import numpy as np |
以上示例中,通过直接对整个DataFrame
或Series
进行操作,避免了显式的循环,并且利用了底层的C实现来提高运算效率。向量化操作通常比迭代方式更高效,并且能够简化代码,提高可读性。
itertools加速循环
itertools
是一个强大的 Python
模块,提供了许多用于迭代和组合的工具函数。虽然 itertools
本身并不会直接加速循环,但可以通过它提供的函数来优化循环的效率。
下面是一些使用 itertools
加速循环的示例:
-
itertools.chain()
: 如果你有多个可迭代对象,可以使用itertools.chain()
将它们连接起来,从而避免多次嵌套循环。
import itertools |
-
itertools.islice()
: 当你只需要迭代可迭代对象的一部分时,可以使用itertools.islice()
来切片迭代。
import itertools |
-
itertools.compress()
: 如果你想根据某个条件过滤迭代对象,可以使用itertools.compress()
来实现。
import itertools |
这只是一些使用 itertools
加速循环的示例。根据具体的需求,你还可以探索其他 itertools
函数,如 itertools.filterfalse()
、itertools.groupby()
等。
Numba makes Python code fast
Numba
是一个适用于使用NumPy
数组、函数和循环的Python
即时编译器。使用Numba
的常见方式是通过应用修饰符来编译函数。当调用使用Numba
修饰的函数时,它会被即时编译为机器码,从而使代码的全部或部分以本地机器码速度运行。
安装
Numba
可用作Anaconda Python 发行版的conda包 :
$ conda install numba |
Numba
也有可用的pip:
$ pip install numba |
装饰器@jit
来尝试和加速一些功能
from numba import jit |
其他感兴趣的事情:
Numba
有很多装饰器,我们已经看到了@jit
,但也有:
-
@njit
- 这是一个别名,因为@jit(nopython=True)
它很常用! -
@vectorize
- 生成NumPy
ufunc
(支持所有方法ufunc
)。文档在这里。 -
@guvectorize
- 生成NumPy
广义ufunc
s。 文档在这里。 -
@stencil
- 声明一个函数作为类似模板操作的内核。 文档在这里。 -
@jitclass
- 对于 jit 感知类。文档在这里。 -
@cfunc
- 声明一个用作本机回调的函数(从C/C++
等调用)。文档在这里。 -
@overload
- 注册您自己的函数实现以在 nopython 模式下使用,例如@overload(scipy.special.j0)
. 文档在这里。
一些装饰器中可用的额外选项:
ctypes/cffi/cython
互操作性:
GPU 目标:
Numba
可以针对Nvidia CUDA GPU。您可以用纯 Python 编写内核并让 Numba 处理计算和数据移动(或明确地执行此操作)。单击以获取有关 CUDA的 Numba 文档。