列表生成式(List Comprehensions)
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)):
|
1
2
|
>>>
list
(
range
(
1
,
11
))
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
]
|
生成[1x1,2x2,3x3,...,10x10] :
|
1
2
3
|
>>> L
=
[]
>>>
for
x
in
range
(
1
,
11
):
L.append(x
*
x)
|
|
1
2
|
>>> L
[
1
,
4
,
9
,
16
,
25
,
36
,
49
,
64
,
81
,
100
]
|
另一种简化的写法:
|
1
2
|
>>> [x
*
x
for
x
in
range
(
1
,
11
)]
[
1
,
4
,
9
,
16
,
25
,
36
,
49
,
64
,
81
,
100
]
|
写列表生成式时,把要生成的元素 x*x 放到前面,后面跟for循环,就可以把list创建出来:
|
1
2
|
>>> [x
*
x
for
x
in
range
(
1
,
11
)
if
x
%
2
=
=
0
]
[
4
,
16
,
36
,
64
,
100
]
|
|
1
2
|
>>> [m
+
n
for
m
in
'ABC'
for
n
in
'XYZ'
]
[
'AX'
,
'AY'
,
'AZ'
,
'BX'
,
'BY'
,
'BZ'
,
'CX'
,
'CY'
,
'CZ'
]
|
列出当前目录下的文件:
|
1
2
3
|
>>>
import
os
>>> [d
for
d
in
os.listdir(
'.'
)]
[
'DLLs'
,
'Doc'
,
'include'
,
'Lib'
,
'libs'
,
'LICENSE.txt'
,
'NEWS.txt'
,
'python.exe'
,
'pythonw.exe'
,
'README.txt'
,
'Scripts'
,
'students'
,
'tcl'
,
'Tools'
]
|
|
1
2
3
4
5
6
7
|
>>> d
=
{
'x'
:
'A'
,
'y'
:
'B'
,
'z'
:
'C'
}
>>>
for
k,v
in
d.items():
print
(k,
'='
,v)
x
=
A
z
=
C
y
=
B
|
|
1
2
|
>>> [k
+
'='
+
v
for
k,v
in
d.items()]
[
'x=A'
,
'z=C'
,
'y=B'
]
|
把一个list 中的字符窜变成小写
|
1
2
3
|
>>> l
=
[
'Aadss'
,
'Bsad'
,
'Casd'
,
'Dasd'
]
>>> [s.lower()
for
s
in
l]
[
'aadss'
,
'bsad'
,
'casd'
,
'dasd'
]
|
|
1
2
3
4
5
6
7
8
9
10
11
12
|
L1
=
[
'Hello'
,
'World'
,
18
,
'Apple'
,
None
]
# 期待输出: ['hello', 'world', 'apple']
print
(L2)
>>> L2
=
[s.lower()
for
s
in
L1
if
isinstance
(s,
str
)]
>>> L2
[
'hello'
,
'world'
,
'18'
,
'apple'
]
>>> [
isinstance
(s,
str
)
and
s.lower()
or
s
for
s
in
l1]
[
'hello'
,
'world'
,
'18'
,
'apple'
,
None
]
>>> [s.lower()
if
isinstance
(s,
str
)
else
s
for
s
in
l1]
[
'hello'
,
'world'
,
'18'
,
'apple'
,
None
]
|
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
|
1
2
3
4
5
6
|
>>> L
=
[x
*
x
for
x
in
range
(
10
)]
>>> L
[
0
,
1
,
4
,
9
,
16
,
25
,
36
,
49
,
64
,
81
]
>>> g
=
(x
*
x
for
x
in
range
(
10
))>>>
g
<generator
object
<genexpr> at
0x02C9FC10
>
|
创建 L 和 G 的区别仅在于最外层的 【】 和 () ,L 是一个 list ,而 G 是一个 generator。
要打印generator 的每一个元素我们需要用到next():
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
>>>
next
(g)
0
>>>
next
(g)
1
>>>
next
(g)
4
>>>
next
(g)
9
>>>
next
(g)
16
>>>
next
(g)
25
>>>
next
(g)
36
>>>
next
(g)
49
>>>
next
(g)
64
>>>
next
(g)
81
>>>
next
(g)
Traceback (most recent call last):
File
"<pyshell#53>"
, line
1
,
in
<module>
next
(g)
StopIteration
|
当计算到最后一个元素时,跑出 StopIteration 错误。
当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
>>> g
=
(x
*
x
for
x
in
range
(
10
))
>>>
for
n
in
g:
print
(n)
0
1
4
9
16
25
36
49
64
81
|
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>>
def
fib(
max
):
n,a,b
=
0
,
0
,
1
while
n<
max
:
print
(b)
a,b
=
b,a
+
b
n
=
n
+
1
return
'done'
>>> fib(
6
)
1
1
2
3
5
8
'done'
|
要把 上面的 fib 函数改为 generator 仅需要把 print(b) 改为 yield b 就可以了:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
>>>
def
fib(
max
):
n,a,b
=
0
,
0
,
1
while
n<
max
:
yield
b
a,b
=
b,a
+
b
n
=
n
+
1
return
'done'
>>> f
=
fib(
6
)
>>> f<generator
object
fib at
0x02C9FD28
>>>>
for
n
in
f:
print
(n)
1
1
2
3
5
8
|
最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
举个简单的例子,定义一个generator,依次返回数字1,3,5:
|
1
2
3
4
5
6
7
|
>>>
def
odd():
print
(
1
)
yield
1
print
(
2
)
yield
3
print
(
3
)
yield
5
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
>>> o
=
odd()
>>>
next
(o)
1
1
>>>
next
(odd())
1
1
>>>
next
(o)
2
3
>>>
next
(o)
3
5
>>>
next
(o)Traceback (most recent call last):
File
"<pyshell#104>"
, line
1
,
in
<module>
next
(o)
StopIteration
|
可以看到,odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错。
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>> g
=
fib(
6
)
>>>
while
True
:
try
:
x
=
next
(g)
print
(
'g:'
,x)
except
StopIteration as e:
print
(
'Generator return value:'
,e.value)
break
g:
1
g:
1
g:
2
g:
3
g:
5
g:
8
Generator
return
value: done
|
迭代器
我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:
|
1
2
3
4
5
6
7
8
9
10
11
|
>>>
from
collections
import
Iterable
>>>
isinstance
([],Iterable)
True
>>>
isinstance
({},Iterable)
True
>>>
isinstance
(
'abc'
,Iterable)
True
>>>
isinstance
((x
for
x
in
range
(
10
)),Iterable)
True
>>>
isinstance
(
100
,Iterable)
False
|
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
|
1
2
3
4
5
6
7
8
9
|
>>>
from
collections
import
Iterator
>>>
isinstance
((x
for
x
in
range
(
10
)),Iterator)
True
>>>
isinstance
([],Iterator)
False
>>>
isinstance
({},Iterator)
False
>>>
isinstance
(
'asd'
,Iterator)
False
|
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数:
|
1
2
3
4
|
>>>
isinstance
(
iter
([]),Iterator)
True
>>>
isinstance
(
iter
({}),Iterator)
True
|
为什么list、dict、str、等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,lterator 对象可以被 next() 函数调用并不断返回下一个数据,知道没有数据抛出 stopIteration 错误。可以吧这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过函数next()函数实现按需计算下一格数据,所以 Iterator 的计算是惰性的,只有在需要返回下一个数据时他才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
高阶函数
一个最简单的高阶函数:
|
1
2
3
4
5
|
>>>
def
add(x,y,f):
return
f(x)
+
f(y)
>>> add(
-
5
,
6
,
abs
)
11
|
map()/ reduce()
map()函数接受两个参数,一个是函数,一个是Iterable,map 将传入的函数依次作用到序列的每个元素,并把结果作为新的 Iterator 返回。
我们要把函数 f(x)= x2,要把这个函数作用在一个list[1,2,3,4,5,6,7,8,9]上,就可以用map()实现如下:
|
1
2
3
4
5
|
>>> r
=
map
(f,[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
])
>>>
next
(r)
1
>>>
list
(r)
[
4
,
9
,
16
,
25
,
36
,
49
,
64
,
81
]
|
|
1
2
|
>>>
list
(
map
(
str
,[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]))
[
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
]
|
reduce() ---> reduce 把一个函数作用在一个序列[x1,x2,x3,.....]上,这个函数必须接受两个参数,reduce 把结果继续和序列的下一个元素做积累计算,其效果就是:
|
1
2
|
reduce
(f,[x1,x2,x3,x4])
=
f(f(f(x1,x2),x3),x4)
|
计算一个序列的和,就可以用到reduce实现:
|
1
2
3
|
>>>
from
functools
import
reduce
>>>
def
add(x,y):
return
x
+
y
|
|
1
2
|
>>>
reduce
(add,[
1
,
3
,
5
,
7
,
9
])
25
|
当然也可以用python内建函数sum(),没必要用reduce。
|
1
2
|
>>>
sum
([
1
,
3
,
5
,
7
,
9
])
25
|
如果把序列[1,3,5,7,9]变换成整数13579.reduce就可以派上用场:
|
1
2
3
|
>>>
from
functools
import
reduce
>>>
def
fn(x,y):
return
x
*
10
+
y
|
|
1
2
|
>>>
reduce
(fn,[
1
,
3
,
5
,
7
,
9
])
13579
|
filter -----> Python内建的filter()函数用于过滤序列。
filter()也接受一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
eg:在一个list中,删掉偶数,只保留奇数:
|
1
2
|
>>>
def
is_odd(n):
return
n
%
2
=
=
1
|
|
1
2
|
>>>
list
(
filter
(is_odd,[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]))
[
1
,
3
,
5
,
7
,
9
]
|
删除字符串中的空字符:
|
1
2
|
>>>
def
not_empty(s):
return
s
and
s.strip()
|
|
1
2
|
>>>
list
(
filter
(not_empty,[
'A'
,'
','
B
',None,'
C
','
']))
[
'A'
,
'B'
,
'C'
]
|
回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()滤掉非回数:
|
1
2
3
4
5
6
7
8
9
10
|
>>>
print
(
list
(
filter
(
lambda
n:
str
(n)
=
=
str
(n)[::
-
1
],
range
(
1
,
2000
))))
#先把数字转换为字符串,然后翻转字符串,最后比较
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
11
,
22
,
33
,
44
,
55
,
66
,
77
,
88
,
99
,
101
,
111
,
121
,
131
,
141
,
151
,
161
,
171
,
181
,
191
,
202
,
212
,
222
,
232
,
242
,
252
,
262
,
272
,
282
,
292
,
303
,
313
,
323
,
333
,
343
,
353
,
363
,
373
,
383
,
393
,
404
,
414
,
424
,
434
,
444
,
454
,
464
,
474
,
484
,
494
,
505
,
515
,
525
,
535
,
545
,
555
,
565
,
575
,
585
,
595
,
606
,
616
,
626
,
636
,
646
,
656
,
666
,
676
,
686
,
696
,
707
,
717
,
727
,
737
,
747
,
757
,
767
,
777
,
787
,
797
,
808
,
818
,
828
,
838
,
848
,
858
,
868
,
878
,
888
,
898
,
909
,
919
,
929
,
939
,
949
,
959
,
969
,
979
,
989
,
999
,
1001
,
1111
,
1221
,
1331
,
1441
,
1551
,
1661
,
1771
,
1881
,
1991
]
|