Python编程 - 三器一包

目录

前言

一、迭代器

(一)基本概念

(二)迭代器和可迭代对象

(三)创建迭代器

(四)内置迭代器函数

(五)优点和局限性

二、生成器

(一)基本概念

(二)创建生成器

(三)生成器表达式

(四)生成器的优势

(五)使用生成器

(六)生成器的应用

(七)生成器和列表对比

三、装饰器

(一)装饰器的基本概念

(二)带参数的装饰器

(三)应用场景

(四)保持元数据

(五)总结

四、闭包

(一)简介

(二)闭包的结构

(三)工作原理

(四)应用场景

(五)注意事项

(六)闭包中修改外部变量

(七)总结

五、总结


前言

上篇文章将了python多态,类属性等知识,这篇文章了解一下python的三器一包:迭代器、生成器、装饰器和闭包


一、迭代器

Python的迭代器是一个重要的概念,特别是在处理序列数据和流数据时。迭代器是一种可以逐一遍历集合中所有元素的对象。

(一)基本概念

迭代器是实现了__iter__()__next__()方法的对象。

  • __iter__(): 这个方法返回迭代器对象本身。它在一个对象被迭代时会被自动调用,可以在循环或其他迭代环境中使用。
  • __next__(): 这个方法返回迭代中的下一个值。当序列遍历结束时,它会引发StopIteration异常,通知迭代终止。

(二)迭代器和可迭代对象

在Python中,有两种与迭代有关的对象类型:可迭代对象迭代器

  • 可迭代对象(Iterable):任何可以返回一个迭代器的对象都被称为可迭代对象。常见的可迭代对象包括列表、元组、字典、集合和字符串。可迭代对象实现了__iter__()方法。

  • 迭代器(Iterator):是一个有状态的对象,它会在调用__next__()时返回序列中的下一个值。迭代器对象实现了__iter__()__next__()方法。

(三)创建迭代器

可以通过实现__iter__()__next__()方法来手动创建一个迭代器。也可以通过iter()函数将一个可迭代对象转换为迭代器。

# 自定义迭代器示例
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index < len(self.data):
            value = self.data[self.index]
            self.index += 1
            return value
        else:
            raise StopIteration

# 使用迭代器
my_iter = MyIterator([1, 2, 3])
for item in my_iter:
    print(item)

(四)内置迭代器函数

python提供了一些内置函数来处理迭代器:

  • iter(): 返回一个迭代器对象。对于可迭代对象,iter()函数将其转换为迭代器。
  • next(): 通过调用迭代器的__next__()方法来获取下一个元素。如果没有元素可返回,则会引发StopIteration异常。
#示例
numbers = [1, 2, 3]
iterator = iter(numbers)
print(next(iterator))  # 输出: 1
print(next(iterator))  # 输出: 2
print(next(iterator))  # 输出: 3

(五)优点和局限性

  • 优点
    • 延迟计算:迭代器在需要时才生成元素,有助于节省内存。
    • 简化代码:通过迭代器,代码更简洁,容易处理无限序列。
  • 局限性
    • 一次性使用:迭代器一旦耗尽(遍历完),无法复用,必须重新创建。
    • 无法反向迭代:标准迭代器仅支持从前到后遍历,不能逆向。

二、生成器

生成器是一种特殊的迭代器,它能够在需要时生成值,从而使得处理大型数据集或流数据变得更加高效。生成器通过使用 yield 关键字创建,并且具有延迟计算的特性,即惰性求值,只有在迭代时才会生成值。下面详细介绍生成器的相关概念和使用方法。

(一)基本概念

生成器(Generator) 是一种函数,它在每次调用时都会生成一个值,并在其 yield 语句的地方暂停执行,下一次迭代从暂停的位置继续。与普通函数不同,生成器函数在执行完所有 yield 语句后会自动退出。

生成器的关键特性包括:

  • 惰性求值:生成器不会一次性生成所有值,而是按需生成,这对于处理大数据集或无限序列非常有用。
  • 状态保持:生成器函数在暂停时保持其执行状态(包括局部变量、指针等),并在下一次调用时继续执行。

(二)创建生成器

生成器通过定义一个包含 yield 语句的函数来创建。yield 会暂停函数的执行并返回一个值,当生成器的 __next__() 方法被调用时,函数会从暂停处继续执行。

# 生成器函数示例
def my_generator():
    print("First yield")
    yield 1
    print("Second yield")
    yield 2
    print("Third yield")
    yield 3

# 使用生成器
gen = my_generator()
print(next(gen))  # 输出: First yield \n 1
print(next(gen))  # 输出: Second yield \n 2
print(next(gen))  # 输出: Third yield \n 3

(三)生成器表达式

python 提供了一种简洁的生成器定义方式,称为 生成器表达式。生成器表达式类似于列表推导式,但返回的是一个生成器对象,而不是一个列表。

# 生成器表达式示例
gen_expr = (x * x for x in range(5))

for value in gen_expr:
    print(value)

在上面的示例中,gen_expr 是一个生成器,它在每次迭代时按需生成平方数。

(四)生成器的优势

生成器相比于普通函数和数据结构有许多优点:

  • 节省内存:生成器按需生成值,不会一次性将所有数据存储在内存中,非常适合处理大型或无限数据集。
  • 代码简洁:生成器表达式可以用一行代码创建生成器,减少了代码量。
  • 惰性求值:生成器只有在需要时才计算值,提高了效率,尤其是处理需要延迟计算的场景。

(五)使用生成器

除了使用 next() 函数来迭代生成器,还可以通过以下方式控制生成器:

  • send(value): 向生成器发送一个值,并暂停生成器的当前位置恢复执行。
  • close(): 终止生成器,生成器在下一次调用 __next__()send() 时会引发 StopIteration 异常。
  • throw(type, value=None, traceback=None): 在生成器中引发指定的异常,生成器可以捕获这个异常,并决定是继续还是终止。
def controlled_generator():
    while True:
        value = yield
        if value is None:
            break
        print(f'Received: {value}')

gen = controlled_generator()
next(gen)  # 初始化生成器
gen.send('Hello')  # 输出: Received: Hello
gen.send('World')  # 输出: Received: World
gen.close()  # 关闭生成器

(六)生成器的应用

生成器在Python中有许多实际应用,以下是一些常见的场景:

  • 数据流处理:生成器可以逐行读取文件,或按需处理数据流,减少内存消耗。
  • 延迟计算:当需要对大数据集进行计算但不想一次性加载时,生成器可以按需计算结果。
  • 管道处理:生成器可以连接在一起形成数据处理管道,每个生成器处理数据的一部分。

(七)生成器和列表对比

特性生成器列表
内存使用按需生成,节省内存一次性加载所有元素,占用大量内存
执行方式惰性求值,逐步生成立即求值,一次性生成所有元素
适用场景大数据集、流数据小数据集,频繁访问元素
代码复杂度简洁,适合管道处理简单明了,适合频繁访问

通过生成器,可以使Python程序在处理大规模数据时更加高效,特别是在内存受限或需要流式处理的场景下。生成器不仅提供了强大的功能,还保持了代码的简洁和可维护性。


三、装饰器

python的装饰器是一个强大的特性,允许你以优雅的方式修改函数或类的行为。装饰器可以用来插入额外的功能、修改函数行为,甚至是对函数进行包装而不直接修改其代码。

(一)装饰器的基本概念

装饰器是一个函数,接受另一个函数作为参数,返回一个新的函数。这个新的函数通常会在原函数的调用之前或之后执行额外的代码。装饰器本质上是一种“包装”机制,使得你可以在不修改原始函数代码的情况下,添加额外的功能。

示例:

def my_decorator(func):
    def wrapper():
        print("有些事情在调用方法之前发生")
        func()
        print("有些事情在调用方法之后发生")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

在该示例中:

  1. my_decorator 是一个装饰器函数,它接受 func 作为参数。
  2. wrapper 函数在调用 func 之前和之后打印了一些信息。
  3. @my_decorator 是装饰器的应用方式,相当于 say_hello = my_decorator(say_hello)
  4. 当你调用 say_hello() 时,实际执行的是 wrapper 函数,其中包括了原始的 say_hello 函数的调用。

(二)带参数的装饰器

装饰器也可以接受参数。要实现带参数的装饰器,你需要创建一个嵌套的装饰器函数:

def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()

在这个例子中:

  1. repeat 是一个接受参数 num_times 的外部函数。
  2. decorator 是实际的装饰器函数。
  3. wrapper 是用来包装原函数的内部函数,它会根据 num_times 的值多次调用原函数。

(三)应用场景

装饰器在实际编程中非常有用,常见的应用场景包括:

  • 日志记录:记录函数的调用信息。
  • 权限检查:检查用户是否有权限调用某个函数。
  • 性能计时:测量函数的执行时间。
  • 缓存:缓存函数的返回值,以提高性能。
  • 输入验证:验证函数参数是否符合要求。

(四)保持元数据

使用装饰器时,通常会改变原函数的一些元数据,如名称和文档字符串。可以使用 functools.wraps 来保留这些元数据

示例:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("调用函数之前被执行")
        result = func(*args, **kwargs)
        print("调用函数之后被执行")
        return result
    return wrapper

@my_decorator
def say_hello():
    """Prints 'Hello!'"""
    print("Hello!")

print(say_hello.__name__)  # 输出 'say_hello'
print(say_hello.__doc__)   # 输出 'Prints 'Hello!''

@wraps 装饰器帮助 wrapper 函数保留原函数的元数据(如名称和文档字符串)

(五)总结

装饰器是一种功能强大的工具,可以让你在不修改原始函数代码的情况下,添加额外的功能。理解装饰器的工作原理以及如何创建和使用它们,可以让你写出更加简洁、灵活和可维护的代码。


四、闭包

(一)简介

闭包是一个函数对象,它能记住并访问它所在的词法作用域中的变量,即使在该作用域已经结束时,仍然可以使用这些变量。换句话说,闭包是一种函数,可以捕获其外部环境的变量,使得这些变量即使超出了其正常的生命周期也能在函数内被访问。

闭包是由嵌套函数和自由变量构成的,闭包可以访问这些自由变量,即外部函数作用域中的变量,即使外部函数已经执行完毕。

(二)闭包的结构

一个闭包通常由三部分组成:

  1. 外部函数:定义了一个包含变量的作用域。
  2. 内部函数:嵌套在外部函数中并引用了外部函数的变量。
  3. 闭包环境:内部函数对外部函数作用域中变量的引用,使得这些变量在外部函数结束后仍然有效。

示例:

def outer_function(message):
    def inner_function():
        print(message)
    return inner_function

closure_function = outer_function("Hello, Python!")
closure_function()  # 输出: Hello, Python!

在这个例子中:

  1. outer_function 是外部函数,它接受一个参数 message
  2. inner_function 是嵌套在 outer_function 中的内部函数,它使用了外部函数的变量 message
  3. outer_function 返回 inner_function 时,message 的值仍然被保留并可以在之后调用 closure_function() 时使用,即使 outer_function 已经执行完毕。

(三)工作原理

在上述示例中,虽然 outer_function 已经执行结束,但返回的 inner_function依然“记住”了 message 变量的值。这种机制就是闭包的重要特性,函数会保存它们所在的词法作用域中的变量,使得这些变量可以在函数执行后依然有效。python中的闭包通过函数对象的 __closure__ 属性来实现,这个属性包含了对外部作用域变量的引用。

示例:

def outer_function(message):
    def inner_function():
        print(message)
    return inner_function

closure_function = outer_function("Hello, Python!")
print(closure_function.__closure__)  # 输出闭包中的自由变量
print(closure_function.__closure__[0].cell_contents)  # 输出自由变量的值 "Hello, Python!"

(四)应用场景

闭包在以下场景中非常有用:

  1. 数据隐藏:使用闭包可以隐藏数据,实现类似于面向对象编程中的私有变量的效果。
  2. 函数工厂:创建带有特定参数配置的函数,避免重复写相似逻辑。
  3. 回调函数:在异步编程或事件驱动编程中,闭包可以保持上下文,确保在执行回调时能访问正确的环境。
  4. 装饰器:装饰器的实现原理就依赖于闭包,允许在不改变函数定义的情况下扩展其功能。

(五)注意事项

闭包虽然强大,但也有一些需要注意的地方:

  1. 变量的作用域:闭包只能访问外部函数中的不可变变量,如果你想在内部函数中修改外部变量,必须使用 nonlocal 关键字。
  2. 可能导致内存泄漏:如果闭包引用的外部变量占用较多资源,可能导致内存泄漏,因为这些资源会一直存在,直到闭包被销毁。

(六)闭包中修改外部变量

通常情况下,闭包只能访问外部变量,但不能修改它们。如果需要修改外部函数中的变量,必须使用 nonlocal 关键字:

示例:

def outer_function():
    count = 0
    def inner_function():
        nonlocal count  # 使用nonlocal声明
        count += 1
        print(count)
    return inner_function

closure_function = outer_function()
closure_function()  # 输出 1
closure_function()  # 输出 2

在这个例子中,nonlocal 允许 inner_function 修改 outer_function 中的 count 变量。

(七)总结

python的闭包是一种函数对象,它能够捕获并“记住”外部函数作用域中的自由变量,使得这些变量在外部函数执行结束后依然可用。闭包在许多高级编程场景中非常有用,比如装饰器、回调函数和数据隐藏等。


五、总结

该篇文章主要讲了python的三器一包,迭代器、生成器、装饰器和闭包,每个知识点都有各自的用途,相信大家都能通过这篇文章体会到其中的差异点,欢迎提出宝贵的意见和建议!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值