在Python中,代码不是越多越好,而是越少越好。代码不是越复杂越好,而是越简单越好。一行代码能实现的功能,决不写5行代码。请始终牢记,代码越少,开发效率越高。Python中有非常多实用的高级特性,可以帮助我们实现代码的简洁,本篇逐一介绍它们——切片、列表生成式、Generator生成器、迭代器。
1、切片 Slice
对经常需要获取指定索引范围的操作,用循环十分繁琐,Python提供了切片(Slice)操作符来实现范围取值。list、tuple、字符串,都支持切片操作。
(1)list 切片:对 list 进行各种切片操作。
L = list(range(100))
print('创建0-100的数列:', L)
l1 = L[:10] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print('切片:取前10个元素', l1)
l2 = L[4:8] # [4, 5, 6, 7]
print('切片:取4-8位置的元素', l2)
l3 = L[-1] # 99
print('切片:取最后一个元素', l3)
l4 = L[-10:] # [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
print('切片:取后10个元素', l4)
l5 = L[:10:2] # [0, 2, 4, 6, 8]
print('切片:前10个元素,每两个取一个', l5)
l6 = L[::20] # [0, 20, 40, 60, 80]
print('切片:所有元素,每20个取一个', l6)
l7 = L[:] # 复制L
print('切片:复制L', l7)
(2)tuple 切片:tuple类型,是不可变list,它也支持切片操作,返回的结果仍是tuple类型。
T = (0,1,2,3,4,5,6,7,8,9)
t1 = T[:3] # (0, 1, 2)
print('切片:取前3个元素', t1)
t2 = T[::2] # (0, 2, 4, 6, 8)
print('切片:所有元素,每两个取一个', t2)
(3)字符串 切片:字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串。
S = 'ABCDEFG'
s1 = S[:] # 复制字符串
print('切片:复制字符串', s1)
s2 = S[-5:] # 取后5个字符 CDEFG
print('切片:取后5个字符', s2)
2、迭代(for...in 数据遍历)
- 什么是迭代? 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
- Python中,使用 for...in 来遍历一切可迭代的对象,比如 list、tuple、dict、字符串等。
(1)如何判断一个对象是否可迭代呢?方法是使用collections模块的Iterable类型来判断。
from collections.abc import Iterable
b1 = isinstance('abc', Iterable) # True
b2 = isinstance([1,2,3], Iterable) # True
b3 = isinstance((1,2,3), Iterable) # True
b4 = isinstance({'a':1,'b':2,'c':3}, Iterable) # True
print(b1, b2, b3, b4)
# 证明字符串、list、tuple和dict都是可迭代的、可遍历的。
(2)遍历 dict
- dict的存储不是按照list方式的顺序排列,所以,每次迭代出的结果顺序很可能不一样。
d = {'a':1, 'b':2, 'c':3}
for k in d.keys():
print('遍历dict:key', k)
for v in d.values():
print('遍历dict:value', v)
for k,v in d.items():
print('同时遍历:key - value', k, v)
(3)遍历 list
l = ['apple', 'bus', 'car', 'duck']
for v in l:
print('遍历list:', v)
- 如何在遍历时获取到list的下标索引号?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身了。
L = enumerate(l)
for i,v in L:
print('索引:',i, ',元素:', v)
# 索引: 0 ,元素: apple
# 索引: 1 ,元素: bus
# 索引: 2 ,元素: car
# 索引: 3 ,元素: duck
# 遍历元素中的两个变量
for x,y in [(1,1),(2,2),(3,3),(4,4)]:
print('(x,y):', x, y)
(4)遍历 tuple
for x,y in ((1,2),(2,4),(3,6),(4,8)):
print('(x,y):', x, y)
for i,v in enumerate(((1,2),(2,4),(3,6),(4,8))):
print('遍历tuple', i, v)
(5)遍历字符串
for v in 'hello world':
print('遍历字符串:', v)
for i,v in enumerate('hello world'):
print('遍历字符串:', i, v)
3、列表生成式
- 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的方式。
L1 = list(range(11,21))
print('L1:', L1)
# L1: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
L2 = [x*x for x in range(1,11)]
print('L2:', L2)
# L2: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方。
L3 = [x*x for x in range(1,11) if x%2==0]
print('L3:', L3)
# L3: [4, 16, 36, 64, 100]
- 使用两层循环,生成排列。更多层的循环用得较少。
L4 = [m+n for m in 'ABCD' for n in '1234']
print('L4:', L4)
# L4: ['A1', 'A2', 'A3', 'A4', 'B1', 'B2', 'B3', 'B4', 'C1', 'C2', 'C3', 'C4', 'D1', 'D2', 'D3', 'D4']
- 实例:列出当前目录下所有文件和目录名。
import os
L6 = [d for d in os.listdir('.')]
print('L6:', L6)
# L6: ['1.py', '2.py', '3.py']
- 列表生成式中for循环的对象,还可以是dict对象。
D = {'x':'apple', 'y':'orange', 'z':'banana'}
L7 = [k+'='+v for k,v in D.items()]
print('L7:', L7)
# L7: ['x=apple', 'y=orange', 'z=banana']
- 实例:把list中的字符串元素的字母都转化成小写
S = ['HELLO','WORLD','APPLE','ORANGE']
L8 = [s.lower() for s in S]
print('L8:', L8)
# L8: ['hello', 'world', 'apple', 'orange']
- 列表生成式中,for的前面可以使用if...else。for的后面可以使用if,但不能使else,特别要注意。看下面这个综合的例子:
L9 = [x*2 if x%2==0 else -x for x in range(1,11)]
print('L9:', L9)
# L9: [-1, 4, -3, 8, -5, 12, -7, 16, -9, 20]
- 列表生成式中,for之前的是表达式,for之后的是过滤条件。因此for之后可以使用if...else,而for之后不能使用else。
4、生成器 Generator
- 如果一个list列表中,后面的元素总是由前面元素推算而来,那么我们通常不必创建完整的list,而动态地使用“一边循环一边计算”的机制来实现,以节省大量的内存空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
(1)第一种方法,使用列表生成式 创建generator生成器
- 要创建一个generator生成器,有很多种方法。其中最简单的方法之一,就是把一个列表生成式的 [] 改成 () ,即创建了一个generator。
L = [x*x for x in range(10)] # 列表生成式,创建list列表
print('列表生成式:', L)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
G = (x*x for x in range(5))
print('生成器:', G)
# <generator object <genexpr> at 0x0321BCD8>
- 列表L和生成器G,它们的区别仅仅在于“列表生成式”外面的 [] 和 (),前者是列表,后者是generator。
- 生成器的 next()。每次调用next(G),就计算出G的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
print(next(G)) # 0
print(next(G)) # 1
print(next(G)) # 4
print(next(G)) # 9
print(next(G)) # 16
# print(next(G)) # Traceback (most recent call last):StopIteration
- 使用 for...in 循环来遍历可迭代的generator对象。通过for循环来迭代它,不会出现StopIteration的错误。
G2 = (x*x for x in range(5))
for g in G2:
print('遍历生成器:', g)
(2)第二种方法,使用generator函数创建生成器
- 函数中使用了yield关键字,就是generator函数。调用generator函数,返回一个生成器对象。
# 定义 generator函数
def odd():
yield 1
yield 3
yield 5
return 'done'
G3 = odd()
print('生成器', G) # <generator object <genexpr> at 0x0321BCD8>
for g in G3:
print('G3: ', g)
# 依次打印 1,3,5
(3)实例:用生成器定义著名的斐波拉契数列
- 用生成器定义著名的斐波拉契数列(Fibonacci,除第一个和第二个数外,任意一个数都可由前两个数相加得到)。Fibonacci数列无法使用“列表生成式”来创建,但使用“生成器”来创建将非常地方便。
def fib(max):
n,a,b = 0,0,1
while n<max:
yield b
a,b = b,a+b
n+=1
return 'done'
G4 = fib(6)
for g in G4:
print('G4: ', g)
# 依次打印 1,1,2,3,5,8
(4)generator函数的返回值
- 使用 yield 关键字定义的generator函数,如何才能取到它的return返回值呢?如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中。
def test():
yield 'a'
yield 'b'
return 'done'
G5 = test()
while True:
try:
x = next(G5)
print('G5: ', g)
except StopIteration as e:
print('generator函数的返回值:', e.value) # 'done'
break
5、迭代器 Iterator
- 可以直接作用于for循环的数据类型有以下几种:一类是集合数据类型,如list、tuple、dict、set、str等;一类是generator,包括生成器和带yield的生成器函数。这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
- 生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值。可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
(1)如何判断一个对象是否是Iterable可迭代的?
from collections.abc import Iterable
# 定义生成器函数
def gen():
yield 1
yield 2
return 'done'
# 创建生成器
G = gen()
b1 = isinstance([], Iterable) # True
b2 = isinstance((), Iterable) # True
b3 = isinstance({}, Iterable) # True
b4 = isinstance('', Iterable) # True
b5 = isinstance((x for x in range(10)), Iterable) # True
b6 = isinstance(G, Iterable) # True
print(b1,b2,b3,b4,b5,b6)
(2)如何判断一个对象是不是Iterator生成器对象?
- 生成器都是Iterator对象,但list、dict、str虽然是Iterable可迭代的,但不是Iterator对象。使用 iter() 函数可以把list、dict、str等变成Iterator生成器对象。
from collections.abc import Iterator
b7 = isinstance([], Iterator) # False
b8 = isinstance(iter([]), Iterator) # True
b9 = isinstance('abc', Iterator) # False
b10 = isinstance(iter('abc'), Iterator) # True
print(b7,b8,b9,b10)
(3)你可能会问,为什么list、dict、str等数据类型不是Iterator?
- 这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
- Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列。Python的for循环本质上就是通过不断调用next()函数实现的。
参考资源:
1、廖雪峰Python教程
2、Python官方文档
END!!!
网友评论