高级特性
首先,牢记在python中代码越少,开发效率越高,为此我们需要学习更多Python的高级功能。
1、切片(slice)
1>对于list,取前3个元素。
处理粗暴的取出来与利用for循环取出来,Python可以直接切片。
>>>list[0:3]#会取出下标0,1,2的三个元素,如果第一个是0可以省略[:3]
也支持倒数切片:
>>>list[-2:-1] # 倒数第1个和第2个(因为0已经被前面占用啦)
>>>list[:10:2] # 前十个数每2个取一个
>>>list[::5]#所有数每5个取一个
>>>list[:]#复制一个源数据
2>对于tuple一样可以进行这些操作,操作结果依然是tuple
3>字符串'xxxx'被看做一个list,所以字符串也可以使用切片操作
2、迭代
1>给定一个list或者tuple,我们可以for循环遍历这个list或者tuple,这种遍历叫做迭代。
list是有下标的,但是只要是可迭代对象,没下标也行(在这点上抽象度比java高),比如dict也是可以被迭代的。
d={'a':1,'b':2}
for key in d:
print(key)#会打印出abc
因为dict不是按照list的方式顺序排列,所以顺序可能会不一样,默认迭代key如果需要迭代value就用:
for value in d.values()
如果要同时迭代一个字典:for k,v in d.items()
for ch in 'ABC' : #ch会迭代字符串
2>判断一个对象是否可迭代:
利用collections模块的iterable 类型判断:
from collections import iterable
isinstance('abc',iterable)#会返回bool值
3>最后一个小问题,如果对list实现类似java的小标循环呢?(就是让下标出现)
Python提供了enumerate函数,可以把list变成索引-元素对,这样就可以在for里迭代索引:
for i,value in enumerate(['A','B','C'])
print(i,value) #同时迭代两个变量,这个写法在python里很常见
#比如
for x,y in [(1,2),(2,4),(3,9)]
print(x,y)
3、列表生成式:
list comprehensions,是Python内置的简单而强大的创建list 的生成式。
-比如:
>>>list(range(1,11))
-或者
for x in range(1,11)
L.append(x*x) #迭代式和函数式不是一个概念
-但是上面如果直接用列表生成式就:list[x*x for x in range(1,11) if x%2==0] #这个式子还用上了条件表达式使得仅仅筛选出偶数的平方
-两层循环成全排列 [m+n for m in 'ABC' n in 'XYZ']
-比如,列出当前目录所有文件和目录名
import os
[d for d in os.listdir('.')]#os.listdr可以列出文件和目录
-for循环多个变量可以用d.items迭代整个字典,因此列表生成式也可以用两个变量生成list:
d={'x':'a','y':'b'}
[k+'='+v for k,v in d.items()]
#输出x=a y=b
#把list中的字符串变成小写
L=['AAA','VVV']
[s.lower() for s in L] #条件放在最后,最后会都变成小写
4、生成器:
列表的容量是有限的,如果创建一个100万个元素的列表,不仅白白占用存储空间,而且如果仅仅需要几个元素,那后面的元素就白费了。为了解决这个问题,生成器就是这种在循环中不断推断出后续元素,用于一边循环一边计算的机制。
1>创建方法:
把list的方法变成圆括号。
g=(x*x for x in range (10))
>>>g 是generator object <genexpr> at 一个地址
2>取值方法
next()函数,每next()一次就会获得一个返回值。
generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,一直到计算出最后一个元素,但是这个使用办法不太友好。正确方法是使用for循环,因为generator也是可迭代对象。
g=(x*x for x in range(10)) #在这里获取的返回对象是x*x也就是接下来的n
for n in g #这个时候打印的是x*x
print(n)
3>generator 非常强大,如果推算的算法比较复杂,for循环没有办法还可以用函数来实现,比如除了第一个数和第二个数以外所有数都是前两个纸盒的斐波那契数列。
def fib(max):
n,a,b=0,0,1
while n < max:
print(b)
a,b = b,a + b #就是分头赋值,两个等式
n = n+1
return 'done' #用函数打印出来的办法
a,b=b,a +b
#相当于
t = (b,a+b) #tuple
a = t[0]
b = t[1] #a=b,b=a+b #但是不用显式的写出临时变量t
#fib函数定义了推算规则,从第一个开始写到后续任意的,这种逻辑和generator很像
接下来应该利用fib函数来制作generator:(只要把print改成yield(把内容填充进去)就可以):
def fib(max):
n,a,b=0,0,1
while n < max:
yield(b)#改编在这
a,b = b,a + b #就是分头赋值,两个等式
n = n+1
return 'done' #用函数打印出来的办法
a,b=b,a +b
#相当于
t = (b,a+b) #tuple
a = t[0]
b = t[1] #a=b,b=a+b #但是不用显式的写出临时变量t
#fib函数定义了推算规则,从第一个开始写到后续任意的,这种逻辑和generator很像
一旦一个函数中有yield关键字,这个函数就不再是一个普通函数,而是一个generator,变成generator的函数,在每次调用next的时候执行,遇到yield语句返回,再次执行的时候从yield之后继续执行。
比如:
def odd():
yield (1)
yield (3)
yield (4)
o=odd()
>>>next(o)
1
>>>next(o)
3
#就这样调用一次next就依次输出到底4次会报错stopIteration
可以看到odd是一个generator,在执行中遇到yield就中断,下次继续执行,执行3次yield之后,已经没有yield可以执行了,所以第4次调用next(o)会报错
回到fib的例子,如果循环不断调用yield,就没法中断,控制办法是利用for:
for n in fib(6):#对于一个generator而言,在这个位置的就是返回值,就是被yield(b)推出来的b
print n
但是如果想具体的把这个值拿出来(而不是打印),则需要:
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e: #这个是捕获stopiteration错误,返回值在这个错误的value里
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
5、迭代器
可以直接作为for循环迭代的对象统称为可迭代对象:iterable
iterable可以通过isinstance()进行判断。
>>> 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
。
-
为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break