Python3 | 筑基期, 推导式、迭代器、生成器!

2024-07-29 10:01:44 浏览数 (3)

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ]

0x00 前言简述

描述:前面,我们一起学习了Python3开发中使用最多的函数(Function),相信大家在作者的实践下也已经掌握函数的创建、多参数的传递,以及匿名函数、闭包了吧,这一章我们一起学习Python3编程中的推导式迭代器以及生成器等相关知识,它也是Python编程中进阶常用的部分,对于程序健壮性以及效率都有所帮助,所以说也是需要我们掌握的。

温馨提示:作者后续实践主要在 Ubuntu 24.04 TLS Python 3.12 Jupyter Notebook 环境中运行,若要配置为作者的学习环境,可参考《#AIGC学习之路》专栏中的流程,此外便于看友一起学习Python系列笔记,访问《#Python学习之路》专栏从前往后按照时间进行学习。

温馨提示:若各位看友在其他平台看到此篇文章,一定要关注公众号【全栈工程师修炼指南】进行持续学习!我们一同学习,一起进步,关注后回复【加群】哟!


0x01 推导式 Comprehension

描述: Python 推导式(comprehension:英 / ˌkɒmprɪˈhɛnʃənz强大且简洁的语法)是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体,包括 列表(List)tuple(生成器(generator))字典(Dict)、集合(Set)。不过在使用推导式时,需要注意可读性,尽量保持表达式简洁,以免影响代码的可读性和可维护性。

1.列表推导

描述: 列表推导式(List Comprehensions)也叫列表解析,灵感取值函数式编程语言Haskell,它是一个非常有用和灵活的工具,可以用来动态的创建列表。

语法格式:

代码语言:javascript复制
[表达式 for 变量 in 列表] 
# 或者 
[表达式 for 变量 in 列表 if 条件]
# 或者 
[表达式 for 变量 in 列表 if 条件...if 条件...]

# 或者
[结果值1 if 判断条件 else 结果2  for 变量名 in 原列表 ]

简单示例

代码语言:javascript复制
# 1.求 0 ~ 10 的平方
[x**2 for x in range(10)]
# 或者 
list(x**2 for x in range(10))
# 执行结果: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 2.计算 30 以内可以被 5 整除的整数
multiples = [x for x in range(30) if x % 5 == 0]
print(multiples)
# 执行结果: [0, 5, 10, 15, 20, 25]


# 3.能整除的2,而y不能整除2进行显示
[(x,y) for x in range(10) for y in range(10) if x % 2 == 0 if y % 2 != 0] 
# 等价于
list2 = []
for x in range(10):
    for y in range(10):
        if  (x % 2 == 0) & (y % 2 !=0):
            list2.append((x,y))
print(list2)
# 执行结果: [(0, 1), (0, 3), (0, 5), (0, 7), (0, 9), (2, 1), (2, 3), (2, 5), (2, 7), (2, 9), (4, 1), (4, 3), (4, 5), (4, 7), (4, 9), (6, 1), (6, 3), (6, 5), (6, 7), (6, 9), (8, 1), (8, 3), (8, 5), (8, 7), (8, 9)]

# 4.利用下标对列表元素进行对应输出
slogan = ['1.Jost do it','2.一切皆有了能','3.让编程改变世界']
brand = ['2.李宁','3.全栈工程师修炼指南','1.Nick']
res = [name ':' title[2:] for title in slogan for name in brand if name[0] == title[0]] 
print(res) 
# 执行结果: ['1.Nick:Jost do it', '2.李宁:一切皆有了能', '3.全栈工程师修炼指南:让编程改变世界']


# 5.针对结果值进行判断处理。
list1 = ['python', 'go', 'java']
list2 = [ word.title() if word.startswith('p') else word.upper() for word in list1 ]
print(list2)
# 执行结果: ['Python', 'Go', 'Java']

实践案例:

  • 实例1.利用使用列表推导式和math模块中的gcd函数来找出 1 到 10 中的互质数(值得学习)
代码语言:javascript复制
# 注意:1既不是质数也不是合数
import math
coprimes = [(a, b) for a in range(1, 11) for b in range(a, 1, -1) if math.gcd(a, b) == 1 if a != 1 if b != 1]
print(coprimes)

执行结果:

代码语言:javascript复制
[(3, 2), (4, 3), (5, 4), (5, 3), (5, 2), (6, 5), (7, 6), (7, 5), (7, 4), (7, 3), (7, 2), (8, 7), (8, 5), (8, 3), (9, 8), (9, 7), (9, 5), (9, 4), (9, 2), (10, 9), (10, 7), (10, 3)]

2.元组推导

描述:元组推导式可以利用 range 区间、元组、列表、字典和集合等数据类型,快速生成一个满足指定需求的元组,与列表推导式类似,但使用小括号 () 来表示元组,值得注意的是返回的结果是一个生成器对象而非元组,需要使用tuple内置函数进行转换。

语法格式:

代码语言:javascript复制
( 表达式 for 变量 in Sequence )
# 或者 
( 表达式 for 变量 in Sequence if 条件)

简单示例

代码语言:javascript复制
# 1.使用下面的代码生成一个包含数字 1~9 的元组:
y = (x for x in range(1,10))
# 返回的是生成器对象
print(type(y))
# 使用 tuple() 函数,可以直接将生成器对象转换成元组
print(tuple(y))

# 执行结果:
# <class 'generator'>
# (1, 2, 3, 4, 5, 6, 7, 8, 9)


# 2.求 1 ~ 100 累加和
result=sum(x for x in range(1, 101))
print(result)
# 执行结果: 5050

3.集合推导

在Python中,集合推导式是一种简洁的方法来创建集合,与列表推导式类似,但使用大括号 {} 来表示集合, 只有值,集合推导式允许你在一行代码中生成一个新的集合。

语法格式:

代码语言:javascript复制
( 表达式 for 变量 in Sequence )
# 或者 
( 表达式 for 变量 in Sequence if 条件)

简单示例

4.字典推导

在Python中,字典推导式是一种简洁的方法来创建字典,与列表推导式类似。字典推导式允许你在一行代码中生成一个新的字典。语法上,它类似于列表推导式,但使用大括号 {} 来表示字典,并且生成的是键值对。

语法格式:

代码语言:javascript复制
{ key_expr: value_expr for 变量 in collection }
# 或者 
{ key_expr: value_expr for 变量 in collection if 条件 }

简单示例

代码语言:javascript复制
# 1.使用字符串及其长度创建字典
# 将列表中各字符串值为键,各字符串的长度为值,组成键值对
brand = ['Google','Tencent', 'Alibaba', 'Baidu']
newdict = {key:len(key) for key in brand}
print(newdict)
# 执行结果: 
# {'Google': 6, 'Tencent': 7, 'Alibaba': 7, 'Baidu': 5}


# 2.提供一个包含四个数字的元组,求得平方为值来创建字典:
tupleDemo = (1,2,4,8)
newdict = {x: x**2 for x in tupleDemo}
print(newdict,"n",type(newdict))
# 执行结果: 
# {1: 1, 2: 4, 4: 16, 8: 64} 
# <class 'dict'>


# 3.将现有字典的值进行一些变换来创建一个新的字典,如将所有值翻倍:
original_dict = {'a': 1, 'b': 2, 'c': 3}
doubled_values = {k: v*2 for k, v in original_dict.items()}
print(doubled_values)
# 执行结果: 
# {'a': 2, 'b': 4, 'c': 6}


# 4.从一个字典中筛选出特定条件的键值对:
original_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
filtered_dict = {k: v for k, v in original_dict.items() if v > 2}
print(filtered_dict)
# 执行结果: {'c': 3, 'd': 4}

0x02 迭代器 iterater

描述: 迭代(iterater,也叫魔术方法)是 Python 最强大的功能之一,是访问集合元素的一种方式, 可以记住遍历的位置的对象,适用于字符串列表元组对象。

特点: 从集合的第一个元素开始访问,直到所有的元素被访问完结束,注: 迭代器只能往前不会后退。

迭代器对象有两个基本的方法 iter()next(),当然我们也可以自行创建类以及构造函数来实现迭代器,在类中实现两个方法__iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成, 防止出现无限循环的情况。

代码语言:javascript复制
# 创建一个数字的迭代器,初始值为 1,逐步递增 1:
class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    # 设置在完成指定循环次数后触发 StopIteration 异常来结束迭代  
    if self.a <= 5:
      x = self.a
      self.a  = 1
      return x
    else:
      raise StopIteration

# 初始化迭代器
myclass = MyNumbers()
myiter = iter(myclass)

# 调用迭代器,每次输出叠加。
print(next(myiter), end=" ")  #运行1次 next
print(next(myiter), end=" ")  #运行2次 next
print(next(myiter), end=" ")  #运行3次 next
print(next(myiter), end=" ")  #运行4次 next
print(next(myiter))           #运行5次由于条件或抛出 StopIteration 从而退出迭代

简单示例:

代码语言:javascript复制
# 1.使用迭代器遍历列表
val=[x for x in range(1,5)]
it = iter(val)    # 创建迭代器对象
print(next(it))   # 输出迭代器的下一个元素
print(next(it))   # 输出迭代器的下一个元素
# 或使用常规for语句进行遍历
for x in it:
  print (x, end=" ")  # 输出迭代器的下一个元素即 3, 4

# 执行结果: 验证了迭代器只能往前不会后退。
1
2
3 4 


# 2.使用迭代器遍历元组
e = (i for i in range(10))
next(e)  # 1
next(e)  # 2
next(e)  # 3
next(e)  # 4
next(e)  # 5

for each in e:
  print(each, end = " ")  # 6 7 8 9


# 3.使用迭代器遍历字符串,以及使用try...except异常处理(后续会介绍)
import sys
iterVal = iter("公众号:全栈工程师修炼指南")
while True:
  try:
    print(next(iterVal), end=",")  # 执行结果: 公,众,号,:,全,栈,工,程,师,修,炼,指,南,
  except StopIteration:       # StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况;
    sys.exit()

实践案例:

  • 示例1.使用自定义迭代器实现斐波那契数列
代码语言:javascript复制
class Fibonacci:
  def __init__(self, count):
    self.count = count

  def __iter__(self):
    self.i = 0
    self.a, self.b = 0, 1
    return self     # 返回它自身,实际就是迭代器

  def __next__(self):
      if self.i <= self.count:
        self.i  = 1
        a_old = self.a
        self.a, self.b = self.b, self.a   self.b
        return a_old # 返回斐波那契的值
      else:
        raise StopIteration

# 遍历迭代器对象
for i in Fibonacci(10):
  print(i, end=" ")

# 执行结果: 0 1 1 2 3 5 8 13 21 34 55 

0x03 生成器 generator

在 Python 中,使用了 yield 关键字定义的函数被称为生成器(generator),生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。实际上生成器就是一个迭代器,跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。

其特点是当在生成器函数中使用 yield 语句时,函数的执行将会暂停,并将 yield 后面的表达式作为当前迭代的值返回。然后,每次调用生成器的 next() 方法或使用 for 循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield 语句, 这样使得生成器函数可以逐步产生值,而不是列举出全部。

什么情况下需要使用 yield?

一个函数 f,f 返回一个 list,这个 list 是动态计算出来的(不管是数学上的计算还是逻辑上的读取格式化),并且这个 list 会很大(无论是固定很大还是随着输入参数的增大而增大),这个时候,我们希望每次调用这个函数并使用迭代器进行循环的时候一个一个的得到每个 list 元素而不是直接得到一个完整的 list 来节省内存,这个时候 yield 就很有用。

简单示例:

代码语言:javascript复制
# 1.创建一个简单的生成器函数。
def genratorDemo(num):
  while num > 0:
    yield num
    num -= 1

# 创建生成器对象
generator = genratorDemo(5)
 
# 通过迭代生成器获取值
print(next(generator))  # 输出: 5
print(next(generator))  # 输出: 4
print(next(generator))  # 输出: 3
 
# 使用 for 循环迭代生成器,提供简洁和高效的迭代方式
for value in generator:
    print(value)  # 输出: 2 1

# 2.yield 关键字后面可以加多个数值(可以是任意类型),但返回的值是与yield 声明变量类型有关。
def get():
    m = 0
    n = 2
    l = ['s',1,3]
    k = {1:1,2:2}
    p = ('2','s','t')
    while True:
        m  = 1
        yield m
        yield m ,n ,l ,k ,p
        
it = get()
print(next(it)) # 1
print(next(it)) # (1, 2, ['s', 1, 3], {1: 1, 2: 2}, ('2', 's', 't'))

print(next(it)) # 2
print(type(next(it)),(type(next(it)))  # <class 'tuple'>  <class 'int'> (实际上是3)

实践案例:

  • 示例.使用 yield 实现斐波那契数列。
代码语言:javascript复制
# 方法1:使用生成器函数实现斐波那契数列
def fibonacci(n): 
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a  # 生成器 yield 定义的变量,运行时返回
        a, b = b, a   b
        print('%d,%d' % (a,b))
        counter  = 1

# f 是一个迭代器,由生成器返回生成。
f = fibonacci(10) 

# 遍历迭代器,直至迭代器结束
while True:
    try:
        print(next(f), end=" ")
    except StopIteration:
        sys.exit()

# 输出结果:
0 1,1
1 1,2
1 2,3
2 3,5
3 5,8
5 8,13
8 13,21
13 21,34
21 34,55
34 55,89
55 89,144

特别提醒: 可迭代、迭代器、生成器三个概念的联系和区别。

  • 1.可迭代概念范围最大,生成器和迭代器肯定都可迭代,但可迭代不一定都是迭代器和生成器,比如上面说到的内置集合类数据类型。可以认为,在 Python 中,只要有集合特性的,都可迭代。
  • 2.迭代器,迭代器特点是,均可以使用 for...in...next 逐一遍历。
  • 3.生成器,生成器一定是迭代器,也一定可迭代。

至于 Python 中为何要引入迭代器和生成器,从前面学习可知除了节省内存空间外,也可以显著提升代码运行速度。

0 人点赞