Python学习笔记(十) 迭代器、生成器、装饰器

简介: Python学习笔记(十) 迭代器、生成器、装饰器

这篇文章主要介绍 Python 中几个常用的高级特性,用好这几个特性可以让自己的代码更加 Pythonic 哦



1、迭代器


什么是迭代呢?简单来说,迭代是访问元素集合的一种方式,关于迭代有两个概念需要我们理解:

  • 可迭代对象 (Iterable):一个实现了 __iter__() 方法的对象
  • 迭代器对象 (Iterator):一个实现了 __iter__()__next__() 方法的对象


由此我们知道,迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象,例如:

列表类型、集合类型是可迭代对象 (有实现 __iter__()),但不是迭代器对象 (没实现 __next__())

而生成器、文件对象是可迭代对象 (有实现 __iter__()),且也是迭代器对象 (有实现 __next__())

from collections.abc import Iterable # 可迭代对象
from collections.abc import Iterator # 迭代器对象
li = [i for i in range(2)]
print(isinstance(li, Iterable)) # True
print(isinstance(li, Iterator)) # False
di = dict()
print(isinstance(di, Iterable)) # True
print(isinstance(di, Iterator)) # False
ge = (i for i in range(2))
print(isinstance(ge, Iterable)) # True
print(isinstance(ge, Iterator)) # True
fi = open('File', 'wt')
print(isinstance(fi, Iterable)) # True
print(isinstance(fi, Iterator)) # True

一般来说,人们会使用 for 循环和 next() 方法遍历可迭代对象和迭代器对象

for 循环可作用于可迭代对象和迭代器对象,而 next() 方法只能作用于迭代器对象

注意,迭代器对象只能迭代一次,并且在迭代结束时,会抛出 StopIteration 异常以表示迭代终止

# 使用 for 循环
li = [i for i in range(2)]
ge = (i for i in range(2))
for i in li:
    print(i, end = ' ') # 0 1 
# for 能自动捕获并处理 StopIteration 异常,以判断迭代终止
for i in ge:
    print(i, end = ' ') # 0 1 


# 使用 next 方法
li = [i for i in range(2)]
ge = (i for i in range(2))
print(next(li)) # TypeError: 'list' object is not an iterator
# next 每次往前读取一个数据,直至到达末尾时抛出 StopIteration 异常
print(next(ge)) # 0
print(next(ge)) # 1
print(next(ge)) # StopIteration


2、生成器


在上面我们可以知道,使用列表生成式可以快速创建一个列表:

li = [i for i in range(100000)]
sys.getsizeof(li) # 412236


但是这样有一个缺点,那就是它会直接生成列表中的所有元素,占用大量内存空间

而使用生成器就能解决这个问题,生成器并不会直接生成全部元素,而是保存一个生成元素的算法

当需要返回结果的时候,生成器才会去计算该元素的值并返回,这种特性也被称为惰性计算或延迟计算

ge = (i for i in range(100000))
sys.getsizeof(ge) # 64


(1)创建生成器


一是使用生成器表达式,它类似于列表生成式,只需将列表生成式中的中括号替换成小括号即可

ge = (i for i in range(5))
print(type(ge)) # <class 'generator'>


二是使用生成器函数,它类似于普通函数,只需将普通函数中的 return 语句替换成 yield 语句即可

def counter(max_num):
    count = 0
    while count < max_num:
        yield count
        count += 1
ge = counter(5)
print(type(ge)) # <class 'generator'>


我们需要将生成器函数的调用赋值给一个变量,那么该变量就是一个生成器

每次调用生成器获取一个元素时,都会执行一次生成器函数中的代码,直至遇到 yield 或者 return 语句

  • yield   语句会返回一个值并挂起函数执行,下一次再调用生成器时会从当前位置继续执行
  • return 语句会马上终止生成器的执行,抛出 StopIteration 异常


(2)调用生成器


一是使用 next() 全局方法,每次调用 next() 方法都会返回一个数据,直至调用结束

ge = (i for i in range(2))
print(next(ge)) # 0
print(next(ge)) # 1
print(next(ge)) # StopIteration


二是使用 send() 内置方法,每次调用 send() 方法都会返回一个数据,并允许向生成器内部发送信息

值得注意的是,第一次调用时需要使用 next() 或者 send(None),不能使用 send() 发送一个其它值

def counter(max_num):
    count = 0
    while count < max_num:
        msg = yield count
        print(msg)
        count += 1
ge = counter(3)
print(ge.send(None))
# 0
print(ge.send('Hello'))
# Hello
# 1
print(ge.send('World'))
# World
# 2
print(ge.send('!'))
# StopIteration


三是使用 for 循环,实际上生成器也是一种特殊的迭代器

ge = (i for i in range(2))
for i in ge:
    print(i)
# 0
# 1


3、装饰器


在讲解装饰器前,我们先来解释一下什么是闭包?先看闭包的定义:

如果在一个内部函数中对外部作用域(但不是全局作用域)的变量进行引用,那么该内部函数称为闭包

def external(x):
    def internal(y):
        return x + y
    return internal
func1 = external(5)
print(func1(10)) # 15
func2 = external(10)
print(func2(10)) # 20
# 以上面的代码为例,internal 是内部函数,external 是外部函数
# 在内部函数 internal 中对外部作用域(但不是全局作用域)的变量 x 进行引用
# 那么这时我们可以称内部函数 internal 为闭包

装饰器实际上就是一个闭包,它接收一个函数作为参数,返回一个经过装饰的函数

def decorator(func):
    def wrapper(*args, **kwargs):
        print('装饰器 前处理')
        func()
        print('装饰器 后处理')
    return wrapper
@decorator
def func():
    print('原有操作')
func()
# 装饰器 前处理
# 原有操作
# 装饰器 后处理


实际上,我们还可以为一个函数添加多个装饰器,注意观察它们之间的执行顺序:

def decorator1(func):
    def wrapper(*args, **kwargs):
        print('装饰器1 前处理')
        func()
        print('装饰器1 后处理')
    return wrapper
def decorator2(func):
    def wrapper(*args, **kwargs):
        print('装饰器2 前处理')
        func()
        print('装饰器2 后处理')
    return wrapper
@decorator1
@decorator2
def func():
    print('原有操作')
func()
# 装饰器1 前处理
# 装饰器2 前处理
# 原有操作
# 装饰器2 后处理
# 装饰器1 后处理


装饰器常常用于日志功能,下面是一个例子:

import time
from functools import wraps
def logger(func):
    @wraps(func) # 添加 functools.wraps 可以防止原有函数自身的信息丢失
    def wrapper(*args, **kwargs):
        currTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        print(
            '[%s] %s is called, with parameters %s, %s' %
            (currTime, func.__name__, args, kwargs)
        )
        return func(*args, **kwargs)
    return wrapper
@logger
def func(x, y):
   return x + y
res = func(3, 4)
# [2021-03-12 11:50:33] func is called, with parameters (3, 4), {}


装饰器还常用于计时功能,下面是一个例子:

import time
from functools import wraps
def timer(func):
    @wraps(func) # 添加 functools.wraps 可以防止原有函数自身的信息丢失
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
    return wrapper
@timer
def func():
    time.sleep(1)
func()
# 1.0003883838653564



文章知识点与官方知识档案匹配,可进一步学习相关知识

目录
相关文章
|
3天前
|
存储 缓存 Python
Python装饰器
Python装饰器
12 0
|
3天前
|
Python
深入理解Python中的装饰器
在Python编程中,装饰器(decorators)是一种强大的工具,用于增强函数或类的功能而不改变其原始定义。本文将深入探讨装饰器的概念、用法和实际应用,帮助读者更好地理解和利用这一特性。
|
3天前
|
数据安全/隐私保护 Python
Python中的装饰器:提升代码可读性和灵活性
Python中的装饰器是一种强大的编程工具,能够提升代码的可读性和灵活性。本文将深入探讨装饰器的原理和用法,以及如何利用装饰器来简化代码、实现日志记录、权限控制等功能,从而让你的Python代码更加优雅和高效。
|
9天前
|
Python
基于Django的Python应用—学习笔记—功能完善
基于Django的Python应用—学习笔记—功能完善
|
9天前
|
数据安全/隐私保护 Python
Python中的装饰器:提升代码可读性与灵活性
Python中的装饰器是一种强大的工具,可以在不改变函数原有逻辑的情况下,为函数添加额外的功能。本文将介绍装饰器的基本概念和用法,并通过实例演示如何利用装饰器提升代码的可读性和灵活性,使代码更加简洁、易于维护。
|
9天前
|
缓存 大数据 数据处理
Python迭代器、生成器和装饰器探究
【4月更文挑战第2天】 迭代器是遍历集合元素的对象,实现`__iter__()`和`__next__()`方法。示例中自定义迭代器`MyIterator`用于生成整数序列。 - 生成器简化了迭代器实现,利用`yield`关键词实现状态保存,减少内存占用。示例中的`my_generator`函数即为一个生成器。 - 装饰器用于修改函数行为,如日志记录、性能分析。装饰器`my_decorator`在函数调用前后添加额外代码。
23 0
|
10天前
|
大数据 数据处理 开发者
深入理解Python中的迭代器和生成器
Python中的迭代器和生成器是实现高效循环和处理大型数据集的重要工具。本文将深入探讨迭代器和生成器的概念、原理以及在实际开发中的应用场景,帮助读者更好地理解和利用这些强大的工具。
|
2月前
|
索引 Python 容器
解释Python中的迭代器和生成器的优势和劣势。
解释Python中的迭代器和生成器的优势和劣势。
35 2
|
3月前
|
数据处理 Python
Python学习:迭代器与生成器的深入解析
Python学习:迭代器与生成器的深入解析
21 0
|
14天前
|
存储 大数据 Python
「Python系列」Python迭代器与生成器
Python迭代器是一个可以记住遍历的位置的对象。迭代器对象必须实现两个方法,`__iter__()` 和 `__next__()`。字符串、列表或元组等数据类型都是可迭代对象,但它们不是迭代器,因为它们没有实现 `__next__()` 方法。
14 0