Python迭代器、生成器、装饰器

2021/5/16 14:25:19

本文主要是介绍Python迭代器、生成器、装饰器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 1 迭代器
    • 1.1 可迭代对象
    • 1.2 迭代器
    • 1.3 自己实现一个可迭代对象
  • 2 生成器
    • 2.1 生成器的实现方式
  • 3 装饰器
    • 3.1 闭包
    • 3.2 使用例子详细说明闭包
    • 3.3 深度解析闭包
    • 3.4 初识装饰器

1 迭代器

python可以从可迭代对象中获取迭代器

1.1 可迭代对象

  • 概念
    可迭代对象是使用iter内置函数可以获取迭代器的对象,如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。序列都可以迭代;实现了__getitem__方法,并且参数从零开始的索引,这种对象也可以迭代。
# 第一种方式判断对象是不是可迭代对象
print('__iter__' in dir(list))
print('__iter__' in dir(tuple))
print('__iter__' in dir(dict))
print('__iter__' in dir(set))
print('__iter__' in dir(str))
print('__iter__' in dir(int))
print('__iter__' in dir(bool))
print('__iter__' in dir([1,2,3]))

# 输出
True
True
True
True
True
False
False
True
# 第二种方式判断对象是不是可迭代对象
from collections import Iterable

print(isinstance('abc', Iterable))
print(isinstance({1, 2, 3}, Iterable))
print(isinstance(1, Iterable))

输出如下: 
True
True
False

1.2 迭代器

通过内置函数iter(iteratable) 返回可迭代对象的迭代器
next(iterator) 返回可迭代对象的下一个元素或者抛出StopIteration异常

it = iter([1,2,3,4])
next(it)
> 1
next(it)
> 2
next(it)
> 3
next(it)
> 4
next(it)
> StopIteration

1.3 自己实现一个可迭代对象

class MyIter:
    def __init__(self):
        self.storage = [1,2,3]
    def __iter__(self):
        return self
    def __next__(self):
        try:
            return self.storage.pop()
        except Exception as e:
            return e 
mi = MyIter() # mi是一个可迭代对象,因为实现了__iter__方法
it = iter(mi) # it是一个迭代器
next(it) # 执行MyIter的__next__方法

2 生成器

生成器也是迭代器,但更加优雅。使用生成器,我们可以实现与迭代器相同的功能,但不必在类中编写iter()和next()函数
如果迭代器是人类,生成器就是人类中的一种,比如黄种人

2.1 生成器的实现方式

# 方式1 yield
def gen():
	yield 1
	yield 2
	yield 3 
g = gen() # g是一个genenrator对象
next(g)
> 1
next(g)
> 2 
next(g)
> 3 
next(g)
> StopInteration

# 方式2 推导式
li = [i*i for i in range(10000)]
# 这是一句列表推导式,使用列表推导式会把0~9999的平方分别进行平方存储到这个列表中
# 生成器推导式
gen = (i*i for i in range(10000))
gen 
> <generator object <genexpr> at 0x000001FAE47B8CF0>
next(gen)
> 0 
next(gen)
> 1
next(gen)
> 4
# 生成器与列表推导式相比可以节省内存空间,在你需要的时候获取值,而不是一次加载到内存中

3 装饰器

装饰器首先要学的是闭包

3.1 闭包

def out_func(data):
	def inner_func():
		msg = "hello"
		print(f"{msg}-{data}")
	return inner_func

闭包的两个条件:

  • 外部函数返回内部函数
  • 内部函数使用外部函数作用域内的变量

3.2 使用例子详细说明闭包

# >符号开头的代表在命令行或者jupyter下执行命令
# 计算移动平均值的类
class Average():
	def __init__(self):
		self.series = []
	def __call__(self, new_value):
		self.series.append(new_value)
		total = sum(self.series)
		return total/len(self.series)
> avg = Average()
> avg(10)
> 10
> avg(11)
> 10.5

# 使用函数的形式
def make_average():
	series = []
	def average(new_value):
		series.append(new_value)
		total = sum(series)
		return total/len(series)
	return average
> avg = make_average() # avg=average函数
> avg(10) # avg(10) series=[10]  new_value=10 
> 10
> avg(11) # avg(11) series=[10,11]  new_value=11 
> 10.5 

3.3 深度解析闭包

  • 自由变量

例子中的series列表就是一个自由变量,指未在本地作用域中绑定的变量,闭包延申到函数作用域之外,包含自由变量series的绑定

在这里插入图片描述
如图所示,闭包函数通过__code__的co_varnames返回闭包函数作用域内的所有变量,co_freevars返回不在闭包函数作用域内,在外层函数作用域内的变量,也就是“自由变量”
闭包函数的__closure__返回一个cell对象,cell对象是一个列表,获取列表的cell_contents属性可以拿到自由变量的值

  • 自由变量如果是不可变数据类型
def make_average():
    total=0
    count=0
    def average(new_value):
        total += new_value
        count += 1
        return total/count
    return average

avg = make_average()
avg(10)
> UnboundLocalError: local variable 'total' referenced before assignment 

# 先说一下解决方案,python3有一个关键字 nonlocal
def make_average():
    total=0
    count=0
    def average(new_value):
    	nonlocal total,count
        total += new_value
        count += 1
        return total/count
    return average

avg = make_average()
avg(10)
> 10.0 

当内部函数执行total+=new_value,相当于total被重新赋值,将自由变量变为局部变量,total就不是自由变量,就不会被保存到闭包中,所以报错
当使用series列表的时候,list.append(value),并没有改变列表的地址,利用了列表是可变对象的这个事实
nonlocal的作用就是声明这个不是局部变量,而是一个自由变量,这样才会被解释器重新保存到闭包中,对于python2中没有nonlocal这个关键字,只能利用可变对象做为闭包的自由变量。

3.4 初识装饰器

先了解闭包的原理,是学习装饰器的必要条件,话不多说,还是直接上代码,实践才是检验真理的唯一标准,哈哈哈

  • 这是一个最基本的装饰器,例子来自于python3官方网站
def wrap(obj):
    return obj 
    
@wrap
def say_hello():
    return "hello world"

say_hello() # 等同于warp(sayhello)()
> "hello world"
wrap(say_hello) #返回say_hello对象
> <function __main__.say_hello()>
wrap(say_hello)() #执行say_hello
> "hello world"
  • 嵌套函数装饰器
# 嵌套函数的装饰器
def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

def greet():
    print('hello world')

greet = my_decorator(greet) # greet = wrapper(wrapper是调用my_decoraor返回的函数对象),并且把greet放进闭包
greet() # 执行wrapper() 先输出print('wrapper of decorator'),在调用greet(),输出print('hello world')

# 输出
>wrapper of decorator
>hello world

# @语法糖

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

@my_decorator #@是python的语法糖等同于my_decorator(greet)
def greet():
    print('hello world')

greet()

# 输出
>wrapper of decorator
>hello world

  • 带参数的嵌套函数装饰器
# 多层闭包
# repeat重复输出,num指重复输出次数
def repeat(num):
    def my_decorator(func):
    	@functools.wraps(func)# 如果有这句 被装饰函数的__name__是被装饰函数本身的名字,如果没有,__name__不论被装饰函数是谁,都返回wrapper
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator


@repeat(4)
def greet(message):
    print(message)

greet('hello world')

# 输出:
> wrapper of decorator
> hello world
> wrapper of decorator
> hello world
> wrapper of decorator
> hello world
> wrapper of decorator
> hello world

greet.__name__ #输出wrapper
# functools.wrap会保留原函数的元信息
  • 类装饰器

类装饰器主要依赖函数__call__ ,因此我们主要重写__call__即可。

每当调用一个类的实例,函数__call__就会执行一次。

class Count:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print('num of calls is: {}'.format(self.num_calls))
        return self.func(*args, **kwargs)

@Count
def example():
    print("hello world")

example()

# 输出
num of calls is: 1
hello world

example()

# 输出
num of calls is: 2
hello world
  • 嵌套装饰器
    嵌套装饰器执行顺序从里到外
import functools

def my_decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator1')
        func(*args, **kwargs)
    return wrapper


def my_decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator2')
        func(*args, **kwargs)
    return wrapper


@my_decorator1
@my_decorator2
def greet(message):
    print(message)

greet('hello world')  #相当于my_decorator1(my_decorator2(greet('hello world')))

# 输出
execute decorator1
execute decorator2
hello world


这篇关于Python迭代器、生成器、装饰器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程