Python学习笔记之生成器、迭代器和装饰器

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

1、生成器

什么是生成器呢?简单来说,在 Python 中一边循环一边计算的机制称为 生成器(generator)

生成器最大的优点在于它支持延迟操作,所谓的延迟操作是指在需要的时候才进行运算产生结果

我们可以使用以下两种方法 创建生成器

  • 生成器表达式

类似于列表生成式,只需将列表生成式中的中括号替换成圆括号即可

>>> gen = (item for item in range(3))
>>> type(gen)
# <class 'generator'>
  • 生成器函数

同样类似于普通函数,只需将普通函数中的 return 语句替换成 yield 语句即可

>>> def counter(num):
        temp = 0
        while(temp < num):
            temp += 1
            yield temp
            
>>> gen = counter(3)
>>> type(gen)
# <class 'generator'>

我们可以使用以下三种方法 调用生成器

  • 内置方法:__next__()

该方法返回调用生成器的运算结果,每次调用返回一个数据,直到调用结束

注意,生成器只能一直向前迭代,不能回退

>>> gen = (item for item in range(3))
>>> gen.__next__()
# 0
>>> gen.__next__()
# 1
>>> # 在 Python 中有一个全局方法 next(gen),作用与 gen.__next__() 类似
>>> next(gen)
# 2
>>> # 当生成器调用结束后,再次调用会抛出 StopIteration 异常
>>> next(gen)
# StopIteration
  • 内置方法:send()

该方法返回调用生成器的运算结果,同时向生成器内部发送数据

注意,在第一次调用 send() 函数前至少使用一次 next() 或 __next__() 方法

>>> def counter(num):
        temp = 0
        while(temp < num):
            temp += 1
            re = yield temp
            print(re)
            
>>> gen = counter(3)
>>> next(gen)
# 1
>>> gen.send('Hello')
# Hello
# 2
>>> gen.send('World')
# World
# 3
>>> next(gen)
# None
# StopIteration
  • for 循环

生成器也是一个可迭代对象,我们可以使用 for 循环进行遍历生成器中的每一个元素

需要注意的是,生成器只能遍历一次

>>> gen = (item for item in range(3))
>>> for item in gen:
        print(item)

# 0
# 1
# 2

2、迭代器

我们首先来了解一下几个与迭代器相关的概念:

迭代(Iteration):在Python 中,我们可以通过 for 循环遍历 list 或 tuple 等数据类型,这种遍历我们称为迭代

可迭代对象(Iterable):可以直接作用于 for 循环的对象我们称为可迭代对象,一般包括集合数据类型和生成器

迭代器(Iterator):实现了迭代器协议的对象称为迭代器

那么如何判断可迭代对象和迭代器:

  • 一个实现了 __iter__() 方法的对象是可迭代的,一个实现了 __iter__() 方法和 __next__() 方法的对象是迭代器

    由此也可以看出,迭代器一定是可迭代对象,但可迭代对象不一定是迭代器

  • 一个可作用于 for 循环的对象是可迭代的,一个可作用于 next() 方法的对象是迭代器

例如,集合数据类型是可迭代对象,但不是迭代器;生成器是可迭代对象,同时也是迭代器

>>> from collections import Iterable, Iterator
>>> li = [item for item in range(3)]
>>> isinstance(li,Iterable)
# True
>>> isinstance(li,Iterator)
# False
>>> gen = (item for item in range(3))
>>> isinstance(gen,Iterable)
# True
>>> isinstance(gen,Iterator)
# True

3、装饰器

(1)闭包

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

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

>>> def external(x):
        def internal(y):
            return x+y
        return internal

>>> func1 = external(5)
>>> func1(10)
# 15
>>> func2 = external(10)
>>> func2(10)
# 20

例如,在上面的代码中,internal 为内部函数,external 为外部函数,在内部函数 internal 中对外部作用域(但不是全局作用域)的变量 x 进行引用,那么这时我们可以称内部函数 internal 为闭包

(2)装饰器

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

from functools import wraps

def decorator(func):
    @wraps(func) # 添加 functools.wraps 可以防止原有函数自身的信息丢失
    def wrapper(*args, **kwargs):
        print('装饰器')
        func()
        print('装饰器')
    return wrap

@decorator
def func():
    print('原有操作')
        
func()

# 装饰器
# 原有操作
# 装饰器

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

from functools import wraps

def decorator1(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('装饰器1')
        func()
        print('装饰器1')
    return wrap

def decorator2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('装饰器2')
        func()
        print('装饰器2')
    return wrap

@decorator1
@decorator2
def func():
    print('原有操作')

func()

# 装饰器1
# 装饰器2
# 原有操作
# 装饰器2
# 装饰器1

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

from functools import wraps

def logger(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(func.__name__ + ' is called')
        return func(*args, **kwargs)
    return wrapper

@logger
def func(x,y):
   return x + y

res = func(3,4)

# func is called

装饰器还可以用于计时功能,下面是另外一个例子:

from functools import wraps
import time

def timer(func):
    @wraps(func)
    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

猜你喜欢

转载自www.cnblogs.com/wsmrzx/p/10547055.html