列表生成式(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 ]
|