在Python中,推导和生成器都是非常常用的,而且它们都是经过极度优化的操作,速度非常快,另外一点是它们的代码可读性也比较强,非常适合用来替代显式的for循环。
当然,有些可能对推导器和生成器不太熟悉,这里我们先介绍一下。
推导器在生成列表时经常会用到,采用方括号来表示,并且里面用一个for循环来生成所需数据。
data = [x for x in range(10)]
data2 = [x for x in range(10) if x % 2 == 0]
这种写法就等效于:
data = []
for i in range(10):
data.append(i)
可以很明显看出,两种方法的功能一样,但第一种写法更加简洁。
生成器跟推导器的语法类似,但把方括号[]改成圆括号(),下面代码中g
就是一个生成器。
g = (x for x in range(10))
<generator object <genexpr> at 0x122ba9a50>
生成器遵守迭代器协议,是逐个生产出元素,而不是创建一个完整的列表,然后把列表传递到某个构造函数中,所以生成器有个很大的好处就是节省内存。
下面我们用推导器和生成器
比如计算从1加到100000,使用for循环和推导器、生成器的代码如下:
n = 1000000
def loop():
res = []
for i in range(n):
res.append(i)
return res
def comprehesion():
return sum([i for i in range(n)])
def generator():
return sum(i for i in range(n))
运行时间对比:
%timeit loop()
90.5 ms ± 833 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit comprehesion()
82.6 ms ± 931 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit generator()
73 ms ± 2.46 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
可以看出,使用推导器和生成器的运行速度都要比显示for循环的方式要快。
在生成字典时,使用推导的方式,效率也会更高一些。
def loop_dict():
res = {}
for i in range(n):
res[i] = i
return res
def comprehesion_dict():
return {i: i for i in range(n)}
%timeit loop_dict()
101 ms ± 1.85 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit comprehesion_dict()
92.1 ms ± 2.32 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
当推导器配合filter和map使用时,效果更佳,看下面的例子:
def map_comprehension(nums):
a = [n * 2 for n in nums]
b = [n **2 for n in a]
c = [n ** 0.33 for n in b]
return max(c)
def map_normal(nums):
a = map(lambda n: n * 2, nums)
b = map(lambda n: n ** 2, a)
c = map(lambda n: n ** 0.33, b)
return max(c)
使用memory_profiler分析两个函数占用的内存情况:
%load_ext memory_profiler
nums = range(100000)
%memit map_comprehension(nums)
peak memory: 148.05 MiB, increment: 10.34 MiB
%memit map_normal(nums)
peak memory: 138.57 MiB, increment: 0.00 MiB
可以看到,第一种方法占用了10.34MB的内存,第二个方法占用的内存是0MB,就是因为第二种方法使用了迭代模式。
欢迎关注微信公众号:Quant_Times