友链提交
请认真填写以下信息,谢谢!

博客信息

HoshinoAi
(请填写完整的网址,例如:https://www.example.com)
(贵站展示本站链接的页面地址,一般是友链页面,填写后将自动验证友链关系有效性)
(用于抓取文章)
(用于接收通知)
菜单
本页目录

四、模块和包/迭代器/生成器/装饰器

4.1 模块与包

4.2 迭代器

在Python中,迭代器(Iterator)是一种对象,它能够被next()函数调用并返回序列中的下一个元素。迭代器对象在每次调用next()时都会返回下一个值,直到没有更多的元素,此时会引发一个StopIteration异常。

迭代器必须实现两个方法:

  • iter():返回迭代器本身,这是为了让迭代器对象能够出现在迭代上下文中。
  • next():返回序列中的下一个项目。

4.2.1 特点

  • 惰性求值:迭代器是惰性的,这意味着它们在需要时才计算下一个值,而不是一开始就计算所有的值。
  • 状态保持:迭代器内部维护一个状态,以便它知道下一次调用__next__()时返回哪个元素。
  • 一次性:迭代器是一次性的,一旦迭代完毕,就不能再从头开始迭代。

4.2.2 用途

  • 遍历数据结构:迭代器用于遍历集合(如列表、元组、字典、集合等)中的元素。
  • 高效数据处理:对于大数据处理,迭代器可以节省内存,因为它不需要在内存中存储整个数据集。
  • 自定义数据流:可以创建自己的迭代器来控制迭代过程。

4.2.3 迭代器案例

# 定义一个名为MyIterator的类,该类将实现迭代器协议
class MyIterator:
    # 构造函数,初始化迭代器的起始和结束值
    def __init__(self, start, end):
        self.current = start  # 当前值,初始设置为start
        self.end = end  # 结束值,迭代器将在这个值之前停止

    # 实现__iter__方法,返回迭代器对象本身
    # 这使得对象能够被用在迭代上下文中,如for循环
    def __iter__(self):
        return self

    # 实现__next__方法,返回序列中的下一个项目
    def __next__(self):
        # 检查当前值是否小于结束值
        if self.current < self.end:
            number = self.current  # 如果是,保存当前值
            self.current += 1  # 并将当前值递增,为下一次迭代做准备
            return number  # 返回当前值
        else:
            # 如果当前值不再小于结束值,说明迭代完成
            # 抛出StopIteration异常,告诉迭代器没有更多的元素
            raise StopIteration

# 创建MyIterator的实例,设置迭代范围从1到5(不包括5)
my_iter = MyIterator(1, 5)

# 使用for循环遍历迭代器,这将自动处理StopIteration异常
# 并在每次迭代中打印当前的数字
for number in my_iter:
    print(number)

4.2.4 迭代器和可迭代对象

需要注意的是,并不是所有的可迭代对象都是迭代器。可迭代对象(Iterable)是实现了__iter__()方法的对象,这个方法返回一个迭代器对象。大多数内置的数据结构如列表、元组、字典和集合都是可迭代的,但它们本身不是迭代器。

my_list = [1, 2, 3]
my_iterator = iter(my_list)
print(next(my_iterator))  # 输出: 1
print(next(my_iterator))  # 输出: 2

其中my_list是一个可迭代对象,而my_iterator是它的迭代器。

4.3 生成器

在Python中,生成器(Generator)是一种特殊类型的迭代器,它能够按需产生值,而不是一次性构建一个数据列表。生成器是通过一个函数来实现的,这个函数使用yield关键字来产出值,而不是使用return返回值。

4.3.1 定义生成器

4.3.1.1 使用yield的生成器函数

def simple_generator():
    yield 1
    yield 2
    yield 3

其中simple_generator是一个生成器函数。当调用这个函数时,它不会立即执行,而是返回一个生成器对象。每次调用生成器的__next__()方法时,函数会执行到下一个yield语句,并产出相应的值。

4.3.1.2 使用生成器表达式

生成器表达式与列表推导类似,但是使用圆括号而不是方括号。

numbers = (x * 2 for x in range(10))

其中numbers是一个生成器,它将0到9的每个数字乘以2。

4.3.2 使用生成器

生成器可以使用for循环迭代,也可以使用next()函数手动获取下一个值。

4.3.2.1 for循环迭代

# 定义一个生成斐波那契数列的生成器函数
def fibonacci_generator():
    # 初始化两个变量a和b,它们分别代表数列中的连续两个数
    a, b = 0, 1
    # 无限循环,每次循环产出当前的斐波那契数
    while True:
        # 使用yield语句产出当前的斐波那契数
        yield a
        # 更新变量a和b,使它们分别指向数列中的下一个数
        # 这里的操作是:a取值为b,b取值为a+b(即下一个斐波那契数)
        a, b = b, a + b

# 调用生成器函数,得到一个生成器对象
fib = fibonacci_generator()
# 使用for循环遍历生成器产生的斐波那契数
for num in fib:
    # 如果当前的斐波那契数大于100,则跳出循环
    if num > 100:
        break
    # 打印当前的斐波那契数
    print(num)

4.3.2.2 next()函数迭代

# 定义一个生成斐波那契数列的生成器函数
def fibonacci_generator():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 创建生成器对象
fib = fibonacci_generator()

# 使用next()函数手动获取生成器中的下一个值
# 我们将获取前几个斐波那契数,直到数值超过100
try:
    while True:
        num = next(fib)
        if num > 100:
            break
        print(num)
except StopIteration:
    # 当生成器完成时,会抛出StopIteration异常
    # 在这个例子中,由于生成器是无限的,这个异常不会触发
    pass

4.4 装饰器

4.4.1 装饰器介绍

4.4.1.1 什么是装饰器?

装饰器是一种特殊类型的函数,它可以用来修改其他函数的行为。简单来说,装饰器就是一个函数,它接收一个函数作为参数,并返回一个新的函数。

4.4.1.2 为什么需要装饰器?

装饰器可以在不修改原函数代码的情况下,为函数增加额外的功能,提高代码的复用性和可维护性。

4.4.2 装饰器使用

在Python中,使用@符号来表示装饰器。将装饰器放在函数定义上方,即可为该函数添加装饰器。

# 定义一个装饰器函数,它接收一个函数作为参数
def my_decorator(func):
    # 在装饰器内部定义一个包装函数,用于包裹被装饰的函数
    def wrapper():
        # 在执行被装饰的函数之前,先执行一些额外的操作
        print("装饰器添加的功能")
        # 调用被装饰的函数
        func()
    # 返回包装函数
    return wrapper

# 使用@符号将装饰器应用到say_hello函数上
@my_decorator
# 定义一个简单的函数,用于打印"Hello!"
def say_hello():
    print("Hello!")

# 调用被装饰器装饰过的函数
say_hello()


#输出
装饰器添加的功能
Hello!

4.4.2.1 带参数的装饰器

装饰器可以接收参数,以便为不同的函数添加不同的功能。

# 定义一个装饰器工厂函数,它接收一个参数msg
def my_decorator(msg):
    # 在装饰器工厂内部定义一个装饰器函数,它接收一个函数func作为参数
    def decorator(func):
        # 在装饰器内部定义一个包装函数,它不接受任何参数
        def wrapper():
            # 在执行被装饰的函数之前,先打印传入装饰器工厂的msg参数
            print(msg)
            # 调用被装饰的函数
            func()
        # 返回包装函数
        return wrapper
    # 返回装饰器函数
    return decorator

# 使用my_decorator装饰器工厂,并传入"装饰器添加的功能"作为参数
# 这实际上创建了一个装饰器,并将其应用到say_hello函数上
@my_decorator("装饰器添加的功能")
# 定义一个简单的函数,用于打印"Hello!"
def say_hello():
    print("Hello!")

# 调用被装饰器装饰过的函数
say_hello()

#输出
装饰器添加的功能
Hello!

4.4.3 编写装饰器

4.4.3.1 保持原函数的文档和参数

在编写装饰器时,可以使用内置的functools模块中的wraps装饰器来保持原函数的文档和参数。

# 导入functools模块中的wraps装饰器,用于保留原始函数的元信息
from functools import wraps

# 定义一个装饰器函数,它接收一个函数作为参数
def my_decorator(func):
    # 使用@wraps装饰器装饰wrapper函数,以保留func函数的名称、文档字符串、参数列表等
    @wraps(func)
    # 在装饰器内部定义一个包装函数,它接受任意数量和类型的参数
    def wrapper(*args, **kwargs):
        # 在执行被装饰的函数之前,先执行一些额外的操作
        print("装饰器添加的功能")
        # 调用被装饰的函数,并传递所有接收到的参数
        return func(*args, **kwargs)
    # 返回包装函数
    return wrapper

# 使用@符号将装饰器应用到say_hello函数上
@my_decorator
# 定义一个函数,用于打印问候语,它接受一个参数name
def say_hello(name):
    """这是一个打招呼的函数"""
    # 打印包含传入名字的问候语
    print(f"Hello, {name}!")

# 调用被装饰器装饰过的函数,并传入参数"小明"
say_hello("小明")
# 打印say_hello函数的名称,由于使用了@wraps装饰器,这里将打印原始函数的名称而不是wrapper
print(say_hello.__name__)

#输出
装饰器添加的功能
Hello, 小明!
say_hello

这里的关键点是@wraps(func),它是一个装饰器,用于更新wrapper函数的__module__、namequalnamedoc__和__annotations__等属性,使得wrapper在某种程度上表现得像func。这样,当你查看say_hello的属性时,比如__name,你会得到原始函数的名称而不是wrapper的名称。

4.4.3.2 带返回值的装饰器

如果原函数有返回值,装饰器也需要返回相应的值。

# 导入functools模块中的wraps装饰器,用于保留原始函数的元信息
from functools import wraps

# 定义一个装饰器函数my_decorator,它接收一个函数func作为参数
def my_decorator(func):
    # 使用@wraps装饰器来装饰内部的wrapper函数
    # wraps的作用是复制原函数的元信息,如__name__、__doc__等,到wrapper函数
    @wraps(func)
    # 定义一个内部函数wrapper,它将接收任意数量和类型的参数
    def wrapper(*args, **kwargs):
        # 在调用原始函数之前,打印一条消息,这是装饰器添加的功能
        print("装饰器添加的功能")
        # 使用*args和**kwargs调用原始函数func,并返回其结果
        return func(*args, **kwargs)
    # 返回wrapper函数,它现在包装了原始的func函数
    return wrapper

# 使用@语法糖将my_decorator装饰器应用到add函数上
@my_decorator
# 定义一个简单的加法函数add,它接收两个参数a和b,并返回它们的和
def add(a, b):
    return a + b

# 调用被装饰器装饰过的add函数,传入参数1和2
result = add(1, 2)
# 打印add函数的返回结果,即1和2的和
print(result)

#输出
装饰器添加的功能
3

这里的my_decorator是一个装饰器,它可以在不修改add函数内部代码的情况下,为其添加额外的功能(在这个例子中是打印一条消息)。通过@my_decorator语法,add函数被装饰,实际上变成了调用wrapper函数,而wrapper函数在执行add函数之前先打印了消息。*args和**kwargs允许wrapper函数接收任意数量和类型的参数,并将它们传递给add函数。最后,add函数计算1和2的和,并将结果返回,然后被打印出来。