1
0
wiki/Tech/programming-language/Python/入门/数据结构.md

30 KiB
Raw Permalink Blame History

title description keywords tags author date
数据结构 Python 数据结构
Python
数据结构
Python/入门
技术/程序语言
7Wate 2023-08-03

数值

Python 的数值数据类型是用来存储数值的。一旦创建,数值对象的身份和类型是不可改变的。如果你改变了数值对象的值Python 将创建一个新的数值对象。Python 支持三种不同的数值类型:

  • 整型 (int):被用来表示整数,包括正整数和负整数,不带小数点。在 Python 3 中,整数的长度是无限的,可以视为 Python 2 中的 Long 类型。此外布尔类型bool实际上是整型的一种特殊形式其中 True 和 False 分别被表示为 1 和 0。
  • 浮点型 (float)用来表示实数由整数部分和小数部分组成。浮点型还可以使用科学计数法表示。例如2.5e2 表示的是 2.5 乘以 10 的平方,等于 250。
  • 复数 (complex):复数由实部和虚部构成,表示形式可以是 a + bj或者 complex(a,b)。在这里,实部 a 和虚部 b 都是浮点型。
num1 = -100      # 整数
num2 = 200.01    # 浮点数
num3 = 0xA0F     # 十六进制
num4 = 0o37      # 八进制

# 引入库
import math
import random

# 数学常量 PI 和 e
pi = math.pi
e = math.e

# 绝对值
abs_val = abs(num1)

# 向上取整
ceil_val = math.ceil(num2)

# 返回最大数
max_val = max(num1, num2)

# x**y 运算后的值
pow_val = pow(num1, num2)

# 随机数
random_val = random.random()

# x弧度的正弦值。
sin_val = math.sin(pi/2)

字符串

字符串在计算机科学和编程中非常重要。在 Python 程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,我们就创建了一个字符串。

定义

# 在 Python 中,可以使用单引号(')和双引号(")来定义字符串,它们的使用方式完全相同。
s1 = 'hello, world!'
s2 = "hello, world!"

# 使用三个单引号(''')或者三个双引号(""")可以创建一个多行字符串。
s3 = """
hello, 
world!
"""

# 反斜杠(\)可以用来在字符串中插入特殊字符,这被称为转义字符。
s4 = '\n\t\141\u9a86\u660a'

# 在字符串前加上字符 r 可以阻止反斜杠的转义效果。
s5 = r'\n\\hello, world!\\\n' 

# 通过将字符串级联来创建新的字符串。
s6 = "this " "is " "string"

# 字符串是不可变的,这意味着你不能改变一个字符串的内容。
s6[2] = "c"    # 这将引发一个错误

# 字符串可以通过索引进行访问,其中 0 是第一个字符的索引,-1 是最后一个字符的索引。
s1 = 'hello, world!'
print(s1[0])  # 输出h
print(s1[-1])  # 输出:!

# Python也支持字符串的切片操作你可以使用这种方式来获取字符串的一部分。
s1 = 'hello, world!'
print(s1[0:5])  # 输出hello

# ===== 字符串切片示意图 =====
| L | e | t | t | e | r | s |
|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|-7 |-6 |-5 |-4 |-3 |-2 |-1 |

转义

转义字符 描述 示例
\\ 反斜杠符号 print("\\") 输出:\
\' 单引号 print('\'') 输出:'
\" 双引号 print("\"") 输出:"
\n 换行 print("\n") 输出一个新行
\r 回车 print("Hello\rWorld!") 输出:World!
\t 横向制表符Tab print("Hello\tWorld!") 输出:Hello World!
\b 退格 (Backspace) print("Hello \bWorld!") 输出:Hello World!
\f 换页 print("Hello \fWorld!") 输出:Hello World!
\a 响铃 print("\a") 执行后电脑有响声
\000 print("\000") 输出:``
\v 纵向制表符 print("Hello \vWorld!") 输出:Hello World!
\other 其他的字符以普通格式输出
\xhh 二进制数,代表一个 ASCII 字符 print("\x48\x65\x6c\x6c\x6f") 输出:Hello
\ooo 八进制数,代表一个 ASCII 字符 print("\141\142\143") 输出:abc
\uhhhh 16 进制数,代表一个 Unicode 字符 print("\u6211\u4eec") 输出:我们

运算

运算符 描述 示例
+ 连接字符串 "Hello " + "World!" 输出:Hello World!
* 重复字符串 "Hello " * 3 输出:Hello Hello Hello
[] 索引字符串 "Hello"[1] 输出:e
[:] 切片字符串 "Hello"[1:4] 输出:ell
in 成员运算符 - 如果字符串中包含给定的字符返回 True 'H' in 'Hello' 输出:True
not in 成员运算符 - 如果字符串中不包含给定的字符返回 True 'H' not in 'Hello' 输出:False
% 格式化字符串 "Hello, %s" % 'World!' 输出:Hello, World!
f-string 格式化字符串的新方式Python 3.6+ f"Hello, {'World!'} 输出:Hello, World!

f-strings

在 Python 3.6 及以后的版本中,格式化字符串引入了更为简洁的方式,即使用 f 字符作为字符串的前缀创建所谓的格式化字符串字面值f-string。通过 f-string可以在字符串中直接插入变量值或表达式并可以通过冒号和数字来指定输出宽度等格式化选项。

# {a:10} 表示将变量 a 插入到字符串中,并占用 10 个字符的宽度。不足 10 个字符,将使用空格进行填充。

a, b = 5, 10
print(f'{a:10} * {b:10} = {a * b}')

PEP 701 的更新

在 Python 3.11 版本中f-strings 还存在一些限制导致在某些情况下使用起来不够灵活。PEP 701 对 f-strings 进行了一些更新,解除了一些限制,使得 f-strings 可以更灵活地使用,包括以下方面的更新:

在 Python 3.12 版本中,对 f-strings 进行了更新,解除了之前的一些限制,使得 f-strings 更加灵活和强大。更新主要包括以下方面:

引号重用

在 Python 3.11 中f-string 的表达式组件使用与包含 f-string 相同的引号会导致 SyntaxError。而在 Python 3.12 中,可以重用引号,不再限制引号的使用。

songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
result1 = f"This is the playlist: {', '.join(songs)}"
print(result1)
# 输出:'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
多行表达式和注释

在 Python 3.11 中f-string 的表达式必须在一行内定义,即使外部表达式可以跨多行,这会使得代码可读性变差。而在 Python 3.12 中,可以定义跨多行的表达式,并且可以包含注释,提高了代码的可读性。

result2 = f"This is the playlist: {', '.join([
    'Take me back to Eden',  # My, my, those eyes like fire
    'Alkaline',              # Not acid nor alkaline
    'Ascensionism'           # Take to the broken skies at last
])}"
print(result2)
# 输出:'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
反斜杠和 Unicode 字符

在 Python 3.11 中f-string 的表达式不能包含任何反斜杠字符,也不能包含 Unicode 转义序列。而在 Python 3.12 中,可以包含反斜杠和 Unicode 字符,使得表达式更加灵活。

songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']

result3 = f"This is the playlist: {'\n'.join(songs)}"
print(result3)
# 输出:
# This is the playlist: Take me back to Eden
# Alkaline
# Ascensionism

result4 = f"This is the playlist: {'\N{BLACK HEART SUIT}'.join(songs)}"
print(result4)
# 输出:'This is the playlist: Take me back to Eden♥Alkaline♥Ascensionism'

以上代码展示了 Python 3.12 中 f-strings 的更新。可以看到,我们可以在 f-string 中使用引号重用,多行表达式和注释,以及包含反斜杠和 Unicode 字符的表达式,这些更新使得 f-strings 更加灵活和强大,更方便地进行字符串格式化。同时,通过使用 PEG 解析器实现的更新,使得错误消息更加准确,有助于代码调试。

常用方法

函数 描述 示例
len(str) 返回字符串长度 len('hello, world!') 输出结果13
str.capitalize() 返回首字母大写的字符串 'hello, world!'.capitalize() 输出结果Hello, world!
str.title() 返回每个单词首字母大写的字符串 'hello, world!'.title() 输出结果Hello, World!
str.upper() 返回字符串变大写后的拷贝 'hello, world!'.upper() 输出结果HELLO, WORLD!
str.find(sub) 查找子串在字符串中的起始位置,未找到返回 -1 'hello, world!'.find('or') 输出结果8
str.startswith(prefix) 检查字符串是否以指定的前缀开头 'hello, world!'.startswith('He') 输出结果False
str.endswith(suffix) 检查字符串是否以指定的后缀结尾 'hello, world!'.endswith('!') 输出结果True
str.center(width, fillchar) 将字符串以指定宽度居中并在两侧填充指定字符 'hello'.center(10, '*') 输出结果:hello
str.rjust(width, fillchar) 将字符串以指定宽度靠右并在左侧填充指定字符 'hello'.rjust(10, '*') 输出结果:*****hello
str.isdigit() 检查字符串是否由数字构成 'abc123456'.isdigit() 输出结果False
str.isalpha() 检查字符串是否由字母构成 'abc123456'.isalpha() 输出结果False
str.isalnum() 检查字符串是否由字母和数字构成 'abc123456'.isalnum() 输出结果True
str.strip() 返回修剪左右两侧空格之后的字符串 ' jackfrued@126.com '.strip() 输出结果:'jackfrued@126.com'

列表

列表(list)是一种结构化的、非标量类型,它是值的有序序列,每个值都可以通过索引进行标识,定义列表可以将列表的元素放在 [] 中,多个元素用 , 进行分隔,可以使用 for 循环对列表元素进行遍历,也可以使用 [][:] 运算符取出列表中的一个或多个元素。

定义

# 列表定义
list1 = [ 'abcd', 786, 2.23, 'runoob', 70.2 ]
tinylist = [123, 'runoob']

# 输出完整列表
print(list1)  # 输出结果:['abcd', 786, 2.23, 'runoob', 70.2]

# 输出列表第一个元素
print(list1[0])  # 输出结果:'abcd'

# 从第二个开始输出到第三个元素
print(list1[1:3])  # 输出结果:[786, 2.23]

# 输出从第三个元素开始的所有元素
print(list1[2:])  # 输出结果:[2.23, 'runoob', 70.2]

# 输出两次列表
print(tinylist * 2)  # 输出结果:[123, 'runoob', 123, 'runoob']

# 连接列表
print(list1 + tinylist)  # 输出结果:['abcd', 786, 2.23, 'runoob', 70.2, 123, 'runoob']

# 通过循环用下标遍历列表元素
for index in range(len(list1)):
    print(list1[index])

# 通过for 循环遍历列表元素
for elem in list1:
    print(elem)

# 通过 enumerate 函数处理列表之后再遍历可以同时获得元素索引和值
for index, elem in enumerate(list1):
    print(index, elem)

运算

# 创建列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]

# 连接列表,返回一个新的列表
list3 = list1 + list2
print(list3)  # 输出:[1, 2, 3, 4, 5, 6]

# 复制列表,返回一个新的列表
list4 = list1 * 3
print(list4)  # 输出:[1, 2, 3, 1, 2, 3, 1, 2, 3]

# 计算元素个数
length = len(list1)
print(length)  # 输出3

# 判断元素是否在列表中
result = 4 in list1
print(result)  # 输出False

result = 2 in list1
print(result)  # 输出True

# 访问元素可以使用索引索引从0开始
elem = list1[0]
print(elem)  # 输出1

# 切片操作
list5 = list1[1:3]
print(list5)  # 输出:[2, 3]

# 切片时可以指定步长
list6 = list1[0:3:2]
print(list6)  # 输出:[1, 3]

# 切片时可以省略开始或结束索引
list7 = list1[:2]
list8 = list1[1:]
print(list7)  # 输出:[1, 2]
print(list8)  # 输出:[2, 3]

# 反向切片
list9 = list1[::-1]
print(list9)  # 输出:[3, 2, 1]

# 修改元素值
list1[0] = 100
print(list1)  # 输出:[100, 2, 3]

# 列表末尾添加元素
list1.append(4)
print(list1)  # 输出:[100, 2, 3, 4]

# 列表末尾删除元素
list1.pop()
print(list1)  # 输出:[100, 2, 3]

# 指定位置插入元素
list1.insert(1, 200)
print(list1)  # 输出:[100, 200, 2, 3]

# 删除指定位置的元素
del list1[2]
print(list1)  # 输出:[100, 200, 3]

# 清空列表
list1.clear()
print(list1)  # 输出:[]

常用方法

函数 描述
len(list) 返回列表元素的个数。
list.append(x) 在列表末尾添加元素 x
list.insert(i, x) 在指定位置 i 前插入元素 x
list.extend(iterable) 将可迭代对象中的元素逐个添加到列表。
list.remove(x) 删除列表中第一个值为 x 的元素。
list.pop([i]) 移除并返回列表中指定位置 i 处的元素。如果未指定索引,默认删除并返回最后一个元素。
list.clear() 清空列表中的所有元素。
list.index(x[, start[, end]]) 返回列表中第一个值为 x 的元素的索引。如果未找到该元素,会引发 ValueError 异常。
list.count(x) 返回元素 x 在列表中出现的次数。
list.sort(key=None, reverse=False) 对列表进行排序,如果指定参数 key 则按照 key 进行排序,如果指定参数 reverse 则为降序。
list.reverse() 将列表中的元素倒序排列。
list.copy() 返回列表的浅拷贝,即创建一个新列表并复制原列表中的元素。

元组

Python 中的元组是一种容器数据类型,使用小括号 () 定义,类似于列表,但元组的元素不能修改,是不可变的。元组用于存储多个数据,可以包含不同类型的元素。与列表不同,元组在定义时可以省略括号,但在实际使用中建议加上括号,以增强代码的可读性。

img

定义

# 定义元组
t = ('骆昊', 38, True, '四川成都')

# 定义空元组
t1 = ()

# 元组中只包含一个元素时,需要在元素后面添加逗号 `,`,否则括号会被当作运算符使用。
tup1 = (50)          # 不加逗号,类型为整型
tup1 = (50,)         # 加上逗号,类型为元组

# 遍历元组中的值
for member in t:
    print(member)

# 重新给元组赋值
# t[0] = '王大锤'  # TypeError元组不可修改
# 变量t重新引用了新的元组原来的元组将被垃圾回收
t = ('王大锤', 20, True, '云南昆明')

# 将元组转换成列表
person = list(t)

# 将列表转换成元组
fruits_list = ['apple', 'banana', 'orange']
fruits_tuple = tuple(fruits_list)

# 创建一个新的元组
tup1 = (12, 34.56)
tup2 = ('abc', 'xyz')
tup3 = tup1 + tup2

# 删除元组
tup = ('Google', 'Runoob', 1997, 2000)
del tup

运算

# 创建元组
tup1 = (1, 2, 3)
tup2 = (4, 5, 6)

# 连接元组,返回一个新的元组
tup3 = tup1 + tup2
print(tup3)  # 输出:(1, 2, 3, 4, 5, 6)

# 复制元组,返回一个新的元组
tup4 = tup1 * 3
print(tup4)  # 输出:(1, 2, 3, 1, 2, 3, 1, 2, 3)

# 计算元素个数
length = len(tup1)
print(length)  # 输出3

# 判断元素是否在元组中
result = 4 in tup1
print(result)  # 输出False

result = 2 in tup1
print(result)  # 输出True

# 访问元素可以使用索引索引从0开始
elem = tup1[0]
print(elem)  # 输出1

# 切片操作
tup5 = tup1[1:3]
print(tup5)  # 输出:(2, 3)

# 切片时可以指定步长
tup6 = tup1[0:3:2]
print(tup6)  # 输出:(1, 3)

# 切片时可以省略开始或结束索引
tup7 = tup1[:2]
tup8 = tup1[1:]
print(tup7)  # 输出:(1, 2)
print(tup8)  # 输出:(2, 3)

# 反向切片
tup9 = tup1[::-1]
print(tup9)  # 输出:(3, 2, 1)

常用方法

函数 描述
len(tuple) 返回元组中元素的个数。
tuple.count(x) 返回元组中元素 x 出现的次数。
tuple.index(x[, start[, end]]) 返回元组中第一个值为 x 的元素的索引。如果未找到该元素,会引发 ValueError 异常。
tuple + tuple 将两个元组连接成一个新的元组。
tuple * n 将元组重复 n 次。
x in tuple 判断元素 x 是否在元组中,如果在返回 True,否则返回 False
x not in tuple 判断元素 x 是否不在元组中,如果不在返回 True,否则返回 False

集合

集合set是由不重复元素组成的无序容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。

定义

集合可以通过花括号 {}set() 函数创建。注意,创建空集合只能用 set(),不能用 {},因为 {} 创建的是空字典。

# 创建集合
set1 = {1, 2, 3, 4, 5}
set2 = {3, 4, 5, 6, 7}
set3 = {5, 6, 7, 8, 9}

# 判断元素是否在集合内
3 in set1       # True
10 in set1      # False

# 添加元素
set1.add(6)     # {1, 2, 3, 4, 5, 6}
set1.add(7)     # {1, 2, 3, 4, 5, 6, 7}
set1.add(3)     # {1, 2, 3, 4, 5, 6, 7},重复的元素不会被添加

# 删除元素
set1.remove(4)  # {1, 2, 3, 5, 6, 7}
set1.discard(6) # {1, 2, 3, 5, 7}
set1.discard(8) # {1, 2, 3, 5, 7}如果元素不存在discard 方法不会引发异常

# 元素个数
len(set1)       # 5

# 清空集合
set1.clear()    # set()

运算

set1 = {1, 2, 3, 4, 5}
set2 = {3, 4, 5, 6, 7}
set3 = {5, 6, 7, 8, 9}

# 交集
set1 & set2         # {3, 4, 5}

# 并集
set1 | set2         # {1, 2, 3, 4, 5, 6, 7}

# 差集
set1 - set2         # {1, 2}

# 对称差运算(在 set1 或 set2 中,但不会同时出现在二者中)
set1 ^ set2         # {1, 2, 6, 7}

# 判断子集和超集
set2 <= set1        # Falseset2 是 set1 的子集吗?
set3 <= set1        # Falseset3 是 set1 的子集吗?
set1 >= set2        # Falseset1 是 set2 的超集吗?
set1 >= set3        # Falseset1 是 set3 的超集吗?

常用方法

函数 描述
set.add(x) 将元素 x 添加到集合中
set.remove(x) 删除集合中的元素 x如果 x 不在集合中会引发 KeyError
set.discard(x) 删除集合中的元素 x如果 x 不在集合中不会引发异常
set.pop() 随机删除集合中的一个元素,并返回删除的元素
set.clear() 清空集合中的所有元素
len(set) 返回集合中元素的个数
x in set 判断元素 x 是否在集合中,如果在返回 True否则返回 False
set1.isdisjoint(set2) 判断两个集合是否没有共同的元素,如果没有返回 True否则返回 False
set1.issubset(set2) 判断集合 set1 是否是集合 set2 的子集,如果是返回 True否则返回 False
set1.issuperset(set2) 判断集合 set1 是否是集合 set2 的超集,如果是返回 True否则返回 False
set1.union(set2) 返回集合 set1 和 set2 的并集
set1.intersection(set2) 返回集合 set1 和 set2 的交集
set1.difference(set2) 返回集合 set1 和 set2 的差集,即在 set1 中但不在 set2 中的元素
set1.symmetric_difference(set2) 返回集合 set1 和 set2 的对称差集,即在 set1 或 set2 中,但不会同时出现在二者中的元素
set1.update(set2) 将集合 set2 中的元素添加到 set1 中
set1.intersection_update(set2) 将集合 set1 中不在 set2 中的元素删除
set1.difference_update(set2) 将集合 set1 和 set2 的差集中的元素从 set1 中删除
set1.symmetric_difference_update(set2) 将集合 set1 和 set2 的对称差集中的元素添加到 set1 中
set.copy() 返回集合的浅拷贝
set.pop() 随机删除并返回集合中的一个元素

字典

img

字典是另一种可变容器模型,且可存储任意类型对象。字典与列表、集合不同的是,它是一种键 - 值key-value对应的数据结构。每个元素都是由一个键和一个值组成的键值对,键和值通过冒号分开。键必须是唯一的,但值则不必。

img

定义

# 使用大括号创建字典
person = {'name': 'John', 'age': 30, 'city': 'New York'}

# 使用 dict() 创建字典
empty_dict = dict()

# 访问字典中的元素
print(person['name'])  # 输出John
print(person['age'])   # 输出30
print(person['city'])  # 输出New York

运算

# 修改字典中的元素
person['age'] = 31
print(person)  # 输出:{'name': 'John', 'age': 31, 'city': 'New York'}

# 添加新的键值对
person['job'] = 'Engineer'
print(person)  # 输出:{'name': 'John', 'age': 31, 'city': 'New York', 'job': 'Engineer'}

# 删除字典中的元素
del person['city']
print(person)  # 输出:{'name': 'John', 'age': 31, 'job': 'Engineer'}

# 清空字典
person.clear()
print(person)  # 输出:{}

# 遍历字典的键
for key in person:
    print(key)

# 遍历字典的值
for value in person.values():
    print(value)

# 遍历字典的键值对
for key, value in person.items():
    print(key, value)

# 判断键是否存在
if 'name' in person:
    print("Name exists.")

常用方法

方法 描述
keys() 返回字典所有键的列表
values() 返回字典所有值的列表
items() 返回字典所有键值对的元组列表
get(key, default=None) 返回指定键的值,如果键不存在,返回默认值
pop(key) 删除指定键的值,并返回该键对应的值
popitem() 随机删除一个键值对,并返回该键值对
clear() 删除字典中的所有键值对
update(other_dict) 将其他字典的键值对更新到当前字典
copy() 返回字典的浅拷贝(复制字典中的键值对)

推导式

推导式是一种强大的 Python 特性,可以快速、简洁地创建新的数据序列。它可以从一个数据序列构建另一个新的数据序列的结构体。

列表

列表推导式是最常用的推导式,它通过在一个可迭代对象上应用表达式来创建一个新的列表。

# 语法
new_list = [out_exp_res for out_exp in input_list]
new_list = [out_exp_res for out_exp in input_list if condition]

# 示例
multiples = [i for i in range(30) if i % 3 == 0]
print(multiples)  # 输出:[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

字典

# 语法
new_dict = {key_expr: value_expr for value in collection}
new_dict = {key_expr: value_expr for value in collection if condition}

# 示例
dic = {x: x**2 for x in (2, 4, 6)}
print(dic)  # 输出:{2: 4, 4: 16, 6: 36}

集合

# 语法
new_set = {expression for item in sequence}
new_set = {expression for item in sequence if conditional}

# 示例
a = {x for x in 'abracadabra' if x not in 'abc'}
print(a)  # 输出:{'d', 'r'}

元组

# 语法
new_tuple = (expression for item in sequence)
new_tuple = (expression for item in sequence if conditional)

# 示例
a = (x for x in range(1, 10))
print(a)          # 输出:<generator object <genexpr> at 0x7f900dfdd200>
print(tuple(a))   # 输出:(1, 2, 3, 4, 5, 6, 7, 8, 9)

其他

f = [x for x in range(1, 10)]
print(f)

f = [x + y for x in 'ABCDE' for y in '1234567']
print(f)

f = [x ** 2 for x in range(1, 1000)]
print(sys.getsizeof(f))  # 查看对象占用内存的字节数
print(f)

# 生成器表达式,不占用额外内存
f = (x ** 2 for x in range(1, 1000))
print(sys.getsizeof(f))  # 相比列表生成式,生成器不占用存储数据的空间
print(f)
for val in f:
    print(val)

迭代器

可迭代对象是 Python 中的一种数据结构,例如列表、元组、字典、字符串等都是可迭代对象。迭代器是一种特殊的可迭代对象,它可以通过调用 iter() 函数获取,然后使用 next() 函数逐个访问元素。迭代器在其生命周期中,会有四个状态:GEN_CREATEDGEN_RUNNINGGEN_SUSPENDEDGEN_CLOSED

graph LR
    A[生成器已创建] --> B[解释器执行中]
    B --> C[在 yield 表达式处暂停]
    C --> D[解释器执行中]
    D --> C
    D --> E[生成器执行结束]
  • iter():创建迭代器。
  • next():输出迭代器的下一个元素。
s = 'abc'
it = iter(s)

print(it)         # 输出:<str_iterator object at 0x10c90e650>
print(next(it))   # 输出:'a'
print(next(it))   # 输出:'b'
print(next(it))   # 输出:'c'

# 抛出异常
# 迭代器用尽后会引发 StopIteration 异常
try:
    print(next(it))
except StopIteration:
    print("迭代器用尽了。")

生成器

生成器是一种特殊的迭代器,它是一个可以像迭代器一样使用 for 循环来获取元素的函数。生成器函数使用 yield 关键字将一个普通函数改造成生成器函数,yield 相当于 return 但有所不同,yield 虽然返回了值但函数并没有结束,而是暂停在 yield 处,下次调用 next() 或循环迭代时会从 yield 处继续执行。生成器在其生命周期中会有四个状态:GEN_CREATEDGEN_RUNNINGGEN_SUSPENDEDGEN_CLOSED

graph LR
    A[生成器函数被调用] --> B[生成器已创建]
    B --> C[解释器执行中]
    C --> D[在 yield 表达式处暂停]
    D --> C
    C --> E[生成器执行结束]

    F[生成器已创建] --> G[GEN_CREATED]
    G --> H[生成器正在执行]
    H --> I[GEN_RUNNING]
    I --> J[在 yield 表达式处暂停]
    J --> H
    I --> K[生成器执行结束]
    K --> L[GEN_CLOSED]

生成器在其生命周期中,会有如下四个状态:

  • GEN_CREATED生成器已创建还未被激活。
  • GEN_RUNNING解释器正在执行只有在多线程应用中才能看到这个状态
  • GEN_SUSPENDED在 yield 表达式处暂停。
  • GEN_CLOSED生成器执行结束。
def fibonacci(n):
    a, b, counter = 0, 1, 0
    while True:
        if counter > n:
            return
        yield a
        a, b = b, a + b
        counter += 1

for i in fibonacci(10):
    print(i, end=" ")  # 输出0 1 1 2 3 5 8 13 21 34 55