Python中有许多强大的功能,简单实用,接下来一一列举。
切片
这个功能不用说了,实用指数五颗星!list、tuple、string等类型都可以使用切片操作,不多说,直接上代码
>>> l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[5:6]
[5]
>>> l[:5]
[0, 1, 2, 3, 4]
>>> l[5:]
[5, 6, 7, 8, 9]
>>> l[5:-1]
[5, 6, 7, 8]
>>> l[::2]
[0, 2, 4, 6, 8]
>>> l[2::]
[2, 3, 4, 5, 6, 7, 8, 9]
>>> str = "Jack Dauson"
>>> str[-3:]
'son'
>>> str[:]
'Jack Dauson'
>>> str[5:]
'Dauson'
总之就是相当灵活,省去了很多循环操作!
迭代
只要能够用for循环遍历的数据结构,都属于可迭代的!像list、tuple、dict、string都是可以迭代的对象!
list
>>> l = [2,3,4]
>>> for i in l:
print(i)
2
3
4
有一个问题:如果想要得到list里面的索引改怎么办?使用enumerate()可以得到所有可迭代对象的索引及相应的值!
>>> for i,value in enumerate(l):
print(i,value)
0 2
1 3
2 4
string
>>> for letter in 'Jack':
print(letter)
J
a
c
k
dict
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
print(key,d[key])
b 2
a 1
c 3
以上只是最简单的迭代,如果想要仅仅遍历dict的键、值、键值对,应该怎么做呢?
>>> for value in d.values():
print(value)
2
1
3
>>> for key in d.keys():
print(key)
b
a
c
>>> for key,value in d.items():
print(key,value)
b 2
a 1
c 3
>>> for i in d.items():
print(i)
('b', 2)
('a', 1)
('c', 3)
使用d.keys()
、d.values()
、d.items()
可以获得dict的更多内容!
那么,问题来了,我怎么样能够判断一个对象是不是可迭代对象呢?
>>> from collections import Iterable
>>> isinstance([1,2,3],Iterable)
True
>>> isinstance({1:2,2:3},Iterable)
True
>>> isinstance('Jack',Iterable)
True
>>> isinstance(123,Iterable)
False
使用collection模块的Iterable类型可以判断某一对象是否为可迭代对象!
列表生成式
列表生成式,顾名思义,就是用来生成列表的啦!大多数人生成列表,先定义一个l = []
,再使用for
循环将元素一个个append
到list中,毫无美感!!!
简单列表的生成,可以使用list(range(1,100))
,那么如果需要生成[1*1,2*2,3*3...]
呢?不要告诉我循环!
>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
同时,还可以添加一些更多的要素,判断使用符合要求的x参与计算
>>> [x*x for x in range(10) if x%2 == 0]
[0, 4, 16, 36, 64]
使用多重循环还可以生成全排列!
>>> [x+y for x in 'ABC' for y in 'LMN']
['AL', 'AM', 'AN', 'BL', 'BM', 'BN', 'CL', 'CM', 'CN']
>>> [x+y+z for x in 'ABC' for y in 'LMN' for z in 'OPQ' if z == 'Q']
['ALQ', 'AMQ', 'ANQ', 'BLQ', 'BMQ', 'BNQ', 'CLQ', 'CMQ', 'CNQ']
>>>
功能强大吧!这样就可以替代for循环优雅地生成列表
上面的功能总体来说还比较易于接受,接下来要放大招了!
生成器generator
通过列表生成器,我们可以创造一个列表。但是,受到内存容量的限制,列表的容量肯定是有限的,如若我们只需要使用列表中的一部分,就不需要一下子生成很大的列表,直接按需生成就好了!列表生成器提供了这样的功能。
生成器相当于是一个生成列表元素的算法,有了这个算法,就不用一下子生成很多元素了,只需要在需要的时候循环生成就可以了,节省了很多空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
那么,如何创建一个generator呢?只需要把列表生成器的[]
改成()
就可以了!
>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> (x*x for x in range(10))
<generator object <genexpr> at 0x03340C60>
如何获取generator里面的元素呢?可以使用next()
函数生成generator的下一个元素。
>>> 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#52>", line 1, in <module>
next(g)
StopIteration
一直调用next()
函数就可以遍历generator的所有元素,直到抛出StopIteration
错误。
纳尼!一直next()
到结束?太奇怪了!最常用的当然还是要for循环,毕竟generator也是Iterable的对象嘛!
>>> g = (x*x for x in range(10))
>>> for element in g:
print(element)
0
1
4
9
16
25
36
49
64
81
所以,我们创建了一个generator后,基本上永远不会调用next()
,而是通过for循环来迭代它,并且不需要关心StopIteration
的错误。
有时候,推算元素的算法很复杂,并不能通过列表生成器实现,而需要通过函数打印出来,譬如fibonacci数列
>>> def fib(Max):
n = 0
a,b = 0,1
while n < Max:
print(b)
a,b = b,a+b
n = n + 1
>>> fib(5)
1
1
2
3
5
这个时候,我应该如何根据一个函数生成generator呢?
一个函数只要含有yield
关键字,就不再是一个普通函数,而是一个generator!
所以只需要把print(b)
改为yield b
就构造了一个generator
>>> def fib(Max):
n = 0
a,b = 0,1
while n < Max:
yield b
a,b = b,a+b
n = n + 1
>>> g = fib(5)
>>> next(g)
1
>>> next(g)
1
>>> for i in g:
print(i)
2
3
5
看到了没有,在函数的执行过程中,遇到return
才会返回;但是变成了generator的函数,遇到了yield
就会中断,并且返回yield
的参数。
那么,遇到了yield
就会中断并返回,我们能不能通过yield
传一些参数进去呢?
>>> def fib(Max):
n = 0
a,b = 0,1
while n < Max:
e = yield b
print(e)
a,b = b,a+b
n = n + 1
>>> g = fib(6)
>>> next(g)
1
>>> next(g)
None
1
>>> g.send('the third element')
the third element
2
>>> g.send('forth')
forth
3
在上面的generator定义中,使用了e = yield b
语句,这样遇到yield
返回b
的同时,还能够通过g.send(arg)
传入一个参数赋给e
,这样,就可以通过generator进行参数传递了!
>>> g = fib(6)
>>> g.send('the First element')
Traceback (most recent call last):
File "<pyshell#94>", line 1, in <module>
g.send('the First element')
TypeError: can't send non-None value to a just-started generator
>>> g.send(None)
1
注意,看上面的代码,为什么会报错?说明启动一个generator的时候,只能传入None
作为参数,而g.send(None)
就相当于next(g)
!
迭代器
说完了生成器generator,我们来看看迭代器iterator
前面提到,凡是能够用for循环遍历的数据结构和对象统统可以称为可迭代的,即Iterable。我们现在接触到Iterable数据结构有list、tuple、dict、string、set,还有生成器generator。
但是,他们之间有什么区别呢?generator不但可以使用for循环,还可以被next()
函数不断调用并返回下一个值,直到返回StopIteration
错误。
能够被next()
函数不断调用并返回下一个值的对象称为迭代器Iterator。generator是Iterable,同时也是一个Iterator;而list、tuple、str、dict不是Iterator。当然,可以通过isinstance()
判断一个对象是否为Iterator。
>>> from collections import Iterator
>>> isinstance((x*x for x in range(10)),Iterator)
True
>>> isinstance([],Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance("ABC",Iterator)
False
如果想让list、tuple、str、dict等Iterable对象转变成Iterator,应该要怎么做呢?iter()
函数能够让Iterable对象变成Iterator
>>> isinstance(iter("ABC"),Iterator)
True
>>> i = iter("ABC")
>>> next(i)
'A'
>>> next(i)
'B'
>>> next(i)
'C'
那么有人要问了,Iterable和Iterator对象到底有什么区别呢?
实际上,Python中Iterator对象表示的是一个数据流,Iterator对象可以被next()
函数不断调用返回下一个值,直到数据全部被返回。Iterator对象数据流可以被看作是一个序列,但是这个序列的长度是不可提前预知的,因为只能通过next()
或者for循环按照需要调用下一个数据,所以Iterator对象的计算是一种惰性计算。
正式因为这样的特性,Iterator可以表示一个无限大的数据流,譬如全体自然数,而list是无法完成这样的任务的。
后记
python强大的功能有很多,只有时常运用才能使得它成为你优雅代码的一部分。不要只满足于使用简单功能,要多尝试才能多进步。
(By MrHammer 2016-05-19 下午4点 @Hohai Clody)