Python |13—切片, 迭代, 推导式, 迭代器和生成器
本文最后更新于51 天前,其中的信息可能已经过时,如有错误请发送邮件到2031853749@qq.com

切片

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

对一个kist, 取前3个元素, 应该怎么做?

笨方法, 用索引直接取前3个元素.

如果是取前N个元素, 也就是索引为0-(N-1)的元素, 可以用循环.

对这种经常取指定索引范围的操作, 用循环十分繁琐. 因此, Python提供了切片(Slice)操作符, 能大大简化这种操作.

Python 切片是一种访问列表或字符串的简便方法.

切片语法格式:

name[start:stop:step]
  • start: 切片开始的索引, 默认为 0.
  • stop: 切片结束的索引, 默认为列表或字符串的长度.
  • step: 切片的步长, 默认为 1.

示例:

# 切片

my_list = [1, 2, 3, 4, 5]
print(my_list[:3])  # 访问列表的前 3 个元素 [1, 2, 3]
print(my_list[-3:])   # 切片从倒数第 3 个元素开始到结尾 [3, 4, 5]
print(my_list[::2])  # 切片步长为 2 [1, 3, 5]
print(my_list[::-1])  # 切片步长为 -1 [5, 4, 3, 2, 1]
print(my_list[1:4:2])  # 切片步长为 2, 访问列表的第 2, 4 个元素 [2, 4]
print(my_list[:])   # 切片不指定索引, 复制整个列表 [1, 2, 3, 4, 5]

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

迭代

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

在Python中, 迭代是通过for ... in来完成的, 而很多语言比如C语言, 迭代list是通过下标完成的, 比如C代码:

for (i=0; i<length; i++) {
    n = list[i];
}

可以看出, Python的for循环抽象程度要高于C的for循环,因为Python的for循环不仅可以用在list或tuple上, 还可以作用在其他可迭代对象上.

list这种数据类型虽然有下标, 但很多其他数据类型是没有下标的, 但是, 只要是可迭代对象, 无论有无下标, 都可以迭代, 比如dict就可以迭代:

因为dict的存储不是按照list的方式顺序排列, 所以迭代出的结果顺序很可能不一样.

默认情况下for key in dict, dict迭代的是key. 如果要迭代value, 可以用for value in dict.values(), 如果要同时迭代key和value, 可以用for keys, values in dict.items().

dict = {'a': 1, 'b': 2, 'c': 3}
for key in dict:
    print(key)  # 输出 a b c

for value in dict.values():
    print(value)  # 输出 1 2 3

for keys, values in dict.items():
    print(keys, values)  # 输出 a 1 b 2 c 3

由于字符串也是可迭代对象, 因此也可以作用于for循环:

string = 'hello world'
for char in string:
    print(char)  # 输出 h e l l o   w o r l d

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

推导式

Python 推导式是一种独特的数据处理方式, 可以从一个数据序列构建另一个新的数据序列的结构体.

Python 推导式是一种强大且简洁的语法, 适用于生成列表, 字典, 集合和生成器。

在使用推导式时, 需要注意可读性, 尽量保持表达式简洁, 以免影响代码的可读性和可维护性.

Python 支持各种数据结构的推导式:

  • 列表(list)推导式
  • 字典(dict)推导式
  • 集合(set)推导式
  • 元组(tuple)推导式

列表推导式

列表推导式是一种创建列表的简洁方式.

语法格式:

[表达式 for 变量 in 列表] 

[out_exp_res for out_exp in input_list]

或者 [表达式 for 变量 in 列表 if 条件] [out_exp_res for out_exp in input_list if condition]

  • 表达式: 用于生成元素的表达式, 可以是任意有效的 Python 表达式, 如变量赋值, 算术运算, 条件语句, 函数调用等.
  • 变量: 用于迭代的变量, 一般是列表中的元素, 也可以是其他可迭代对象中的元素.
  • 列表: 用于迭代的列表, 可以是任意可迭代对象, 如列表, 元组, 字符串, 字典等.
  • 条件: 用于过滤元素的条件, 只有满足条件的元素才会被包含到结果中.

示例:

# 列表推导式
# 计算列表中每个元素的平方
squares = [x**2 for x in range(1, 11)]
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 列表推导式
# 计算列表中每个元素的平方, 并过滤掉大于 5 的元素
squares = [x**2 for x in range(1, 11) if x**2 <= 25]
print(squares)  # [1, 4, 9, 16, 25]

字典推导式

字典推导式是一种创建字典的简洁方式.

语法格式:

{ key_expr: value_expr for value in collection }

或

{ key_expr: value_expr for value in collection if condition }
  • key_expr: 用于生成键的表达式, 可以是任意有效的 Python 表达式, 如变量赋值, 算术运算, 条件语句, 函数调用等.
  • value_expr: 用于生成值的表达式, 可以是任意有效的 Python 表达式, 如变量赋值, 算术运算, 条件语句, 函数调用等.
  • collection: 用于迭代的集合, 可以是任意可迭代对象, 如列表, 元组, 字符串, 字典等.
  • condition: 用于过滤元素的条件, 只有满足条件的元素才会被包含到结果中.

示例:

# 字典推导式
# 使用字符串及其长度创建字典
string_dict = {s: len(s) for s in ['apple', 'banana', 'orange']}
print(string_dict)  # {'apple': 5, 'banana': 6, 'orange': 6}
# 字典推导式
#提供三个数字,以三个数字为键,三个数字的平方为值来创建字典
num_dict = {num: num**2 for num in [1, 2, 3]}
print(num_dict)  # {1: 1, 2: 4, 3: 9}

集合推导式

集合推导式是一种创建集合的简洁方式.

语法格式:

{ expression for item in Sequence }

或

{ expression for item in Sequence if conditional }

示例:

# 集合推导式
# 计算集合中每个元素的平方
squares = {x**2 for x in range(1, 11)}
print(squares)  # {1, 4, 9, 16, 25, 36, 49, 64, 81, 100}
# 集合推导式
# 判断不是 abc 的字母并输出
letters = {'a', 'b', 'c', 'd', 'e'}
filtered_letters = {letter for letter in letters if letter not in 'abc'}
print(filtered_letters)  # {'d', 'e'}

元组推导式(生成器表达式)

元组推导式是一种创建元组的简洁方式.元组推导式可以利用 range 区间, 元组, 列表, 字典和集合等数据类型, 快速生成一个满足指定需求的元组.

语法格式:

(expression for item in Sequence)

或

(expression for item in Sequence if conditional)

元组推导式和列表推导式的用法也完全相同, 只是元组推导式是用 () 圆括号将各部分括起来, 而列表推导式用的是中括号 [], 另外元组推导式返回的结果是一个生成器对象.

示例:

# 元组推导式
# 生成一个包含数字 1~9 的元组
numbers = (num for num in range(1, 10))
print(numbers)  # <generator object <genexpr> at 0x0000018260D76740>
print(type(numbers))  # <class 'generator'>
print(list(numbers))  # 使用list()函数将生成器对象转换为列表, 输出 [1, 2, 3, 4, 5, 6, 7, 8, 9]

迭代器

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

  • 基本数据类型,如list, tuple, dict, set, str等;
  • 生成器generator, 包括生成器和带yield的generator function.

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable.

可以使用isinstance(name, Iterable)函数判断一个对象是否是可迭代对象.

可迭代对象除了可以用for循环外, 还可以被next()函数调用, 得到一个迭代器对象.

可以被next()函数调用并不断返回下一个值的对象称为迭代器.

可以用isinstance(name, Iterator)函数判断一个对象是否是迭代器.

生成器都是Iterator对象, 但list, dict, str等虽然是Iterable, 却不是Iterator.

把list, dict, str等Iterable变成Iterator可以使用iter()函数:

# 将列表转换为迭代器
my_list = [1, 2, 3, 4, 5]
my_iter = iter(my_list)
print(my_iter)  # <list_iterator at 0x0000018260D76740>
print(type(my_iter))  # <class 'list_iterator'>
print(next(my_iter))  # 1
print(next(my_iter))  # 2

Iterator的计算是惰性的, 只有在需要返回下一个数据时它才会计算.

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

Python的for循环本质上就是通过不断调用next()函数实现的.

for x in [1, 2, 3, 4, 5]:
    pass

完全等价于:

it = iter([1, 2, 3, 4, 5])
while True:
    try:
        x = next(it)
    except StopIteration:
        break

生成器

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

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

要创建一个generator,有很多种方法.

第一种方法很简单, 只要把一个列表生成式的[]改成(), 就创建了一个generator:

L = [x**2 for x in range(10)]
print(L)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x**2 for x in range(10))
print(g)  # <generator object <genexpr> at 0x0000018260D76740>
print(type(g))  # <class 'generator'>

我们可以使用next()函数获取generator的下一个返回值, 直到没有更多的元素时抛出StopIteration错误时退出循环.

当然, 我们也可以用for循环来迭代generator.

generator非常强大, 如果推算的算法比较复杂, 用类似列表生成式的for循环无法实现的时候, 还可以用函数来实现(函数将在后面讲到).

比如, 著名的斐波拉契数列(Fibonacci), 除第一个和第二个数外, 任意一个数都可由前两个数相加得到.

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函数实际上是定义了斐波拉契数列的推算规则, 可以从第一个元素开始, 推算出后续任意的元素, 这种逻辑其实非常类似generator.要把fib函数变成generator函数, 只需要把print(b)改为yield b就可以了.

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

这就是定义generator的另一种方法,.

在 Python 中, 使用了 yield 的函数被称为生成器(generator).

生成器函数是一个带yield关键字的函数, 它返回一个生成器对象.

与普通函数不同的是, 生成器是一个返回迭代器的函数, 它不存储所有结果, 而是每次迭代时生成一个结果.更简单点理解生成器就是一个迭代器.

最难理解的就是generator函数和普通函数的执行流程不一样. 普通函数是顺序执行, 遇到return语句或者最后一行函数语句就返回. 而变成generator的函数, 在每次调用next()的时候执行, 遇到yield语句返回, 再次执行时从上次返回的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'

# 调用生成器函数
g = fib(6)
print(g)  # <generator object fib at 0x0000018260D76740>
print(type(g))  # <class 'generator'>

# 迭代生成器
for n in g:
    print(n)  # 1 1 2 3 5 8

注意:调用generator函数会创建一个generator对象, 多次调用generator函数会创建多个相互独立的generator

生成器函数的优势是它们可以按需生成值, 避免一次性生成大量数据并占用大量内存. 此外生成器还可以与其他迭代工具(如for循环)无缝配合使用, 提供简洁和高效的迭代方式.

本文为2305拾柒原创.
文章作者:拾 柒
文章链接:Python |13—切片, 迭代, 推导式, 迭代器和生成器
版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议. 转载请注明来自拾 柒
如果觉得有用,可以分享出去~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇