高级特性
一、切片(Slice)
取一个list或tuple的部分元素的操作。
# 法一:若截断N个元素,该方法不好用 [L[0], L[1]] # 法二:循环 r = [] n = 4 for i in range(n): # 0~n-1 r.append(L[i]) print(r) # 法三:切片操作符 print(L[0:3]) # 0-2 # 如果第一个索引是0,可以省略 L[:3] L[1:3] # 从1开始,取2个元素 # 同样支持倒数切片 L[-2:] # 倒数第2个到最后 L[-2:-1] # -2到-1,不含-1 A = list(range(100)) #0-99的列表 A[-10:] #后10个数 A[:10] #前10个数 A[10:20] #前11-20个数 10-19 A[:10:2] # 前10个数,每两个取一个 A[::5] #所有数,每5个取一个 A[:] #只写[:]就可以原样复制一个list
tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple
(0,1,2,3,4,5)[:3] # (0, 1, 2) (0,1,2,3,4,5)[2:] # (2, 3, 4, 5) (0,1,2,3,4,5)[::2] # (0, 2, 4)
字符串
'xxx'
也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串'ABCDEFG'[:3] # 'ABC' 'ABCDEFG'[1::2] # 'BDF'
二、迭代
Python中用
for...in...
来完成迭代,可以通过for
循环来遍历list
或tuple
。Python的for循环抽象程度要高于C的for循环,因为Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。
只要是可迭代对象,无论有无下标,都可以迭代,比如
dict
就可以迭代。d = {'a':1, 'b':2, 'c':3} for key in d: # dict默认迭代key print(key) for value in d.values(): # dict用values()可迭代访问value print(value) for k,v in d.items(): # dict同时迭代key和value print(k,'=',v) for ch in 'ABCDEFG' : print(ch)
如何判断一个对象是否是可迭代对象?可以通过
collections.abc
模块的Iterable
类型判断。from collections.abc import Iterable isinstance('abc', Iterable) #str是否可迭代 True isinstance([1,2,3], Iterable) # list是否可迭代 True isinstance(123, Iterable) # 整数不可迭代 False
对list实现下标循环,使用Python内置的
enumerate
函数,把一个list变成索引-元素对,就可以在for循环中同时迭代索引和元素本身for i, value in enumerate(['A', 'B', 'C']): print(i, value) # Python中可以在for循环引用多个变量 for x, y in [(1, 1), (2, 4), (3, 9)]: print(x, y) for x, y, z in [(1, 2, 3), (3, 4, 6), (5, 6, 9)]: print(x, y, z)
迭代查找一个list中的最大值和最小值,并返回一个tuple
li = [1,3,5,9,4,0,6] def fine_min_max(n): if len(n) == 0: return None, None else: min = max = li[0] for i in n : if i > max: max = i if i < min: min = i return (min, max) print(fine_min_max(li)) def find2(L): L1 = L[:] L1 = sorted(L1) max = L1[-1] min = L1[0] return(max, min) print(find2(li))
三、列表生成式
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
list(range(1, 11)) # 生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] L = [] for x in range(1,11): # 生成[1x1, 2x2, 3x3, ..., 10x10] L.append(x*x) # 循环繁琐,用列表生成器 [x * x for x in range(1,11)] [x * x for x in range(1,11) if x % 2 == 0] # 筛选出偶数 # 两层循环全排列 [m + n for m in 'ABC' for n in 'XYZ'] [j+k+l for j in '12345' for k in 'NUSQX' for l in '@#$%&']
运用列表生成式,列出当前目录下的所有文件和目录名。
import os # 导入os模块 [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
列表生成器使用两个变量生成list。
d = {'x':'A','y':'B','z':'C'} [k+'=' + v for k, v in d.items()]
把一个list中所有的字符串变成小写。
str.lower() str.upper() L = ['HELLO', 'World', 'Apple', 'GOOD'] [s.lower() for s in L] [s.upper() for s in L]
列表生成式中的
if...else...
使用。for后面的if是一个筛选条件,不能带else;for前面的部分是一个表达式,它必须根据x计算出一个结果,要带上else。[x for x in range(1, 11) if x % 2 == 0] # [2, 4, 6, 8, 10] [x if x % 2 == 0 else -x for x in range(1, 11)] # [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
四、生成器
一边循环一边计算的机制,称为生成器:generator。通过列表生成式,直接创建一个列表,但是受到内存限制,列表容量肯定是有限的。而且如果在一个占用内存很大的列表中含有很多元素,而只需访问前几个元素,那么绝大多数元素占用的空间就被浪费了。如果列表能够按照某种算法推算出后面的元素,就可以不必创建完整的list从而节省大量空间。
创建生成器的方法
把一个列表生成式的
[]
改成()
,就创建了一个generator。可以通过next()
函数获得generator的下一个返回值,generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。g = (x * x for x in range(10)) next(g) next(g) # 上面不断调用next(g),繁琐 # 正解:用for循环 for n in g: print(n) 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) # 上面的函数和generator仅一步之遥。要把fib函数变成generator函数,只需要把print(b)改为yield b就可以了 def fib2(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' f = fib2(6)
在循环中不断调用
yield
就会不断中断,要给循环设置一个条件来退出循环;同样的把函数改成generator函数后,不用next()获取返回值而是直接用for循环迭代。for n in fib2(6): print(n)
但是用for循环调用generator时,发现拿不到generator的
return
语句的返回值。如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中。g = fib2(6) while True: try: x = next(g) print('g:', x) except StopIteration as e : print('Generator return value:', e.value) break
如果一个函数定义中包含
yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator。generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。def odd(): print('step 1') yield 1 print('step 2') yield 2 print('step 3') yield 3 o = odd() next(o) # step 1 next(o) # step 2 next(o) # step 3 next(odd()) # step 1 next(odd()) # step 1 next(odd()) # step 1 # 原因在于odd()会创建一个新的generator对象,上述代码实际上创建了3个完全独立的generator,对3个generator分别调用next()当然每个都会返回第一个值。
调用该generator函数时,首先要生成一个generator对象,然后用
next()
函数不断获得下一个返回值。
杨辉三角形
def triangles(): L = [1] yield L while True: L = [v+w for v,w in zip([0]+L,L+[0])] yield L #打印三角 for i,row in enumerate(triangles()): print(row) if i>=10: break
五、迭代器
可以直接作用于
for
循环的对象统称为可迭代对象:Iterable
;可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。生成器都是
Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数。from collections.abc import Iterable isinstance([], Iterable) from collections.abc import Iterator isinstance([], Iterator) isinstance({}, Iterator) isinstance('abc', Iterator) isinstance(iter([]), Iterator) isinstance(iter('abc'), Iterator)
Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。