• 沒有找到結果。

8 Python 高级特性

在文檔中 数据结构与算法 (頁 32-39)

掌握了 Python 的数据类型、语句和函数,基本上就可以编写出很多有用的程序了。

例:

构造一个元素为1, 3, 5, 7, ..., 99的列表.

L = []

n = 1

while n <= 99:

L. append (n) n += 2

L = [n for n in range(100) if n % 2 == 1]

但是,Python 代码不是越多越好,而是越少越好;不是越复杂越好,而是越简单越好。

基于这一思想,这一节来介绍一些 Python 中非常有用的高级特性,能一行代码实现的功能,绝不写五行。

请始终牢记,代码越少,开发效率越高。

8.1 切片

取一个 list 或 tuple 的部分元素是非常常见的操作。

例:

给定一个 list

L = ['apple ', 'banana ', 'cherry ', 'grape ', 'peach ']

如何取出其前三个元素?

• 笨办法

r = [L[0] , L[1] , L[2]]

print(r)

• 使用循环

r = []

for i in range(3):

r. append (L[i]) print(r)

• 使用切片 (slice)

在很多编程语言中,针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切 片。Python 没有针对字符串的截取函数,只需要切片一个操作就可以完成,非常简单。

8.2 迭代

如果给定一个 list 或 tuple,我们可以通过for循环来遍历这个 list 或 tuple,这种遍历称为迭代(Iteration)。

只要是可迭代对象,无论有无下标,都可以迭代。

例:

dict 没有下标,但它也可以迭代:

d = {'a': 1, 'b': 2, 'c': 3}

for key in d:

print(key)

a b c

因为 dict 的存储不是顺序排列的,所以迭代出的结果顺序可能会不一样。

例:

因 str 也是可迭代对象,它也可用for循环进行迭代:

for ch in 'abcd ':

print(ch)

a b c d

当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究 竟是 list 还是其他数据类型。

8.2.1 如何判断一个对象是可迭代对象?

可通过 collections 模块的 Iterable 类型判断:

from collections .abc import Iterable print( isinstance('abc ', Iterable ) ) print( isinstance([] , Iterable ) ) print( isinstance(() , Iterable ) ) print( isinstance({} , Iterable ) ) print( isinstance({1} , Iterable ) )

print( isinstance((x for x in range(10)), Iterable ) )

print( isinstance(123 , Iterable ) )

True True True True True False

8.2.2 enumerate()函数

Python 内置的enumerate()可以把一个 list 变成索引-元素对,这样就可以在for循环中同时迭代索引和元素 本身:

for i, value in enumerate([ 'a', 'b', 'c']):

print(f"{i}: { value }")

0: a 1: b 2: c

for i, value in enumerate(( 'a', 'b', 'c')):

print(f"{i}: { value }")

0: a 1: b 2: c

for i, value in enumerate('abc '):

print(f"{i}: { value }")

0: a 1: b 2: c 以上代码中,for循环同时引用两个变量,这在 Python 中非常常见,如 for x, y in [[1 , 1], [2, 4], [3, 9]]:

print(f'{x}, {y}')

1, 1 2, 4 3, 9

8.3 列表生成式

列表生成式即 List Comprehensions,是 Python 内置的非常简单却强大的可以用来创建 list 的生成式。

例: print(L)

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

2. 列表生成式

L = [x * x for x in range(1, 11)]

print(L)

[1, 4, 9, 16, 25, 36, 49, 64, 81,

例:

L = [m + n for m in 'ABC ' for n in 'XYZ ']

print(L)

['AX ', 'AY ', 'AZ ', 'BX ', 'BY', 'BZ ', ' CX ', 'CY ', 'CZ ']

8.3.1 列表生成式的应用 例:

利用os模块列出当前目录下的所有文件和目录名 dirs = [d for d in os. listdir ('.')]

print(dirs)

['lec01.out ', 'lec01 .log ', 'src ', 'tmp.out ', 'lec01 .aux ', 'lec01 .tex ', 'asset ', 'lec01.pdf ', '. DS_Store ', 'makefile ']

例:

将一个 list 中的字符串变为小写

L = ['Hello ', 'Wuhan ', 'University ']

l = [s. lower () for s in L]

print(l)

['hello ', 'wuhan ', 'university ']

例:

将字典{'a': 1, 'b': 2, 'c': 3}转换为列表的形式['a=1', 'b=2', 'c=3']

d = {'a': 1, 'b': 2, 'c': 3}

l = [k + '=' + str(v) for k, v in d. items ()]

print(l)

['a=1 ', 'b=2 ', 'c=3 ']

8.4 生成器

通过列表生成式,可以直接创建一个列表,而受到内存限制,列表容量肯定是有限的。假设创建了一个包含 100 万个元素的列表,而仅需访问前面几个元素,这不仅占用很大的存储空间,并且后面绝大多数元素占用的 空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素 呢?这样就不必创建完整的 list,从而节省大量的空间。在 Python 中,这种一边循环一边计算的机制,称为 生成器:generator。

8.4.1 generator 的创建

• 把列表生成式的[]改为(),即可创建一个 generator:

L = [x *x for x in range(3)]

print(L)

G = (x *x for x in range(3)) print(G)

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

<generator object <genexpr > at 0 x7fd6a02f26d8 >

创建L和G的区别仅在于最外层的[]和(),L是一个 list,而G是一个 generator。

• 使用关键字yield. 例如,生成斐波拉契数列可以这么做 def fib(n):

i, a, b = 0, 0, 1 while i < n:

yield b

a, b = b, a + b i = i + 1

f = fib (6) print(f)

<generator object fib at 0 x7fc4e6b576d8 >

如果一个函数中包含关键字yield,则这个函数就不再是一个普通函数,而是一个 generator.

8.4.2 generator 的使用

• 使用next.

print(next(G)) print(next(G)) print(next(G)) print(next(G)) 0

1 4

Traceback (most recent call last):

File "src/ lec01 /code/ generator .py", line 9, in <module >

print(next(G)) StopIteration

这种方式过于复杂,正确的使用方式是for ... in循环,因为 generator 也是可迭代对象。

• 使用for ... in循环.

for i in fib (4):

print(i)

1 1 2 3

注意,generator 和 function 的执行流程不一样。

• function 是顺序执行,遇到return语句或者最后一行语句就返回。

• 变成 generator 的 function,在每次调用next()时执行,遇到yield语句返回,再次执行时从上次返回 的yield语句处继续执行。

8.5 可迭代对象 (Iterable) 与迭代器 (Iterator)

可以直接作用于 for 循环的数据类型有以下几种:

• 集合数据类型,如 list、tuple、dict、set、str 等;

• generator,包括生成器和带 yield 的 generator function。

这些可以直接作用于 for 循环的对象统称为可迭代对象 (Iterable)。可使用isinstance()来判断一个对象是否 为Iterable对象。

from collections .abc import Iterable print( isinstance('abc ', Iterable ) ) print( isinstance([] , Iterable ) ) print( isinstance(() , Iterable ) ) print( isinstance({} , Iterable ) ) print( isinstance({1} , Iterable ) )

print( isinstance((x for x in range(10)), Iterable ) ) print( isinstance(123 , Iterable ) )

True True True True True True False

可使用next()函数调用并不断返回下一个值的对象称为迭代器 (Iterator). 可使用isinstance()来判断一 个对象是否为Iterator对象。

from collections .abc import Iterator print(isinstance([], Iterator )) print(isinstance({} , Iterator )) print(isinstance('abc ', Iterator ))

print(isinstance((x for x in range(5)), Iterator ))

False False False True

生成器都是 Itertor 对象,但 list、dict、str 虽然是 Iterable 对象,却不是 Iterator。可使用iter()函数将 list、dict、str 等 Iterable 对象变成 Iterator.

print(isinstance(iter([]) , Iterator )) print(isinstance(iter('abc '), Iterator )) True

True

为什么 list、dict、str 等数据类型不是 Iterator 呢?

因为 Python 的 Iterator 对象表示的是一个数据流,它可以被next()函数调用并不断返回下一个数据,直 到没有数据时抛出 StopIteration 错误。可以把这个数据流看做是一个有序序列,但不能提前知道其长度,只 能不断通过next()函数来按需计算下一个数据。因此,Iterator 的计算是惰性的,只有在需要返回下一个数据 时它才计算。Iterator 甚至可以是一个无限大的数据流,如全体自然数;而使用 list 则不可能存储无限个元素。

小结

• 凡是可作用于 for 循环的对象都是 Iterable 类型;

• 凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列。

集合数据类型如 list、dict、str 等是 Iterable 但不是 Iterator,不过可以通过 iter() 函数获得一个 Iterator 对 象。

8.6 习题

1. 编写函数min_and_max(L),求一个 list 的最小值和最大值,并以 tuple 将它们返回。

2. 编写一个 generator,生成杨辉三角。

在文檔中 数据结构与算法 (頁 32-39)

相關文件