何谓List Comprehension
这个功能是以一个特别的方式应用for回圈,并创造出带有元素的List。
应用List Comprehension,除了程式码比较好看,执行效率其实也比较高喔!
情境题
好的,在体验List Comprehension的威力之前,先来用比较基本的方式来产生List。
请创造一个含有整数0~9
的List。
请用for回圈,不要写10行...
如果你有好好练习前几天的语法,你的程式应该会像这样:
arr1 = []
for i in range(10):
arr1.append(i)
print(arr1)
使用List Comprehension
好的,就让我们来看看List Comprehension会怎么做!
我们就来创造一个跟上述一样,含有整数0~9
的List。
范例:
# in-place construction
arr1 = [i for i in range(10)]
print(arr1)
你看看,一行就解决,是不是让人很心动呢!
这边来解释一下,这行程式码要怎么读& 理解。
这行是说我们要创造一个List arr1
。
里面要放什么? 要放变数i
。
变数i
哪里来? 从后面的for回圈来
那for回圈会从range拿出什么呢? 是0~9
。
于是我们就得到一个含有0~9
的List啦!
变化型List Comprehension
除了单纯的从for回圈取得元素之外,List Comprehension还有很多变化型可用
话不多说,范例来:
# in-place construction
arr1 = [i for i in range(10)]
# you can use if to filter the elements
arr2 = [x for x in arr1 if x % 2 == 0]
# you can use as many conditions as you want!
arr3 = [x for x in arr1 if x >= 3 and x % 2]
# use nested for loops to make everyone dizzy XD
arr4 = [(x, y) for x in range(3) for y in range(4)]
print(arr1)
print(arr2)
print(arr3)
print(arr4)
好的,又到了程式码解释时间,事情是这样的:
第二个List Comprehension,我们加上了if条件。arr2
要放什么? 放x
。x
怎么来,从后面的for来。
for从啥提取元素? 从arr1
。
有什么过滤条件吗? 取出的元素至少要通过除以2的余数为0 (即为2的倍数)
于是我们从arr1
取出0~9
,把为2的倍数的元素存入arr2
,故答案为0, 2, 4, 6, 8
。
其实这样的写法就等于告诉Python,要在for回圈内包一层if的意思。
第三个List Comprehension,我们用上了多个条件。
但其实基本概念与第二个很像,除了条件变多以外,没什么大改变,故不多做解释。
第四个List Comprehension,我们用了两个for回圈。
那就跟刚刚说的一样,我们可以想像成第一层for回圈里面又包了一个for回圈。
那就来考考各位,请问arr4
里面总共会有几个元素啊?
答案是12个,你猜对了吗?
List Comprehension的效率
如先前所说,List Comprehension除了在语法上比较简洁优美以外,效率上也是赢过传统的方式。
让我们看看以下范例:
import time
arr1 = []
s = time.time()
for i in range(int(1e+6)):
arr1.append(i)
print('took {} secs'.format(round(time.time() - s, 3)))
s = time.time()
arr2 = [i for i in range(int(1e+6))]
print('took {} secs'.format(round(time.time() - s, 3)))
这个范例做的事情很简单,首先我们引入了time
模组。
并在两次创造List的前后计时,以算出执行所需时间。
由于每个人电脑的效能不同,算出来的时间会不一样。但基本上,使用List Comprehension的方式应该会比传统方法来的快很多。
以下是笔者在自己电脑上执行的结果:
took 0.148 secs
took 0.07 secs
引入模组
既然上面都用到了,那我们就顺便说说如何在Python引入模组吧!
引入模组的方法大概如下:
# standard import
import time
# import and give alias
import random as rd
# precise imoprt
from pathlib import Path
# useless statement
from datetime import *
要点:
- 使用
import
关键字引入模组 - 可引入模组后,用
as
帮他取昵称,这样就可以不用打又臭又长的套件名称XD - 可使用
import ... from ...
的语法,从某套件中拿出特定的功能 - 最后一个方法感觉很糟,会污染全域,建议不要使用XD
List Comprehension背后的原理
让我们来稍微了解一下,List Comprehension这个神奇的东西好了。
先上范例:
# what if we don't assign comprehension to list?
comp = (i for i in range(10))
print(comp)
print(type(comp))
arr1 = list(comp)
arr2 = list(comp)
arr3 = [comp]
print(arr1)
print(arr2)
print(arr3)
先看前三行,我们用了List Comprehension,但没有把它放进List里面,而是把它塞在变数里,之后把他的值与型别印出来看看。
你会发现,其实他是一个叫做generator
的东西,并且印出的时候,无法看到里面的值。
这个东西我们现在不谈,之后再细细说明。
接着我们下面的程式开始执行,猜猜看,arr1
&arr2
的值会不会一样?
执行后会发现,arr2
是空List,原因是generator
这个东西被arr1
消耗掉了,所以arr2
并没有拿到元素!