第十三章 迭代器、生成器、 装饰器

一、可迭代对象

1. 容器类(能存放多个元素的数据类型):

    ① 序列:字符串、列表、元组、字节

    ② 字典

    ③ 集合

    # 组件:开发社区写的一堆类

    

2. 迭代对象iteration

    --- 可进行遍历的对象

    > 可迭代对象都是Iterable的扩展类(子类、衍生类,派生类)

    > 重写了__iter__(self),可以返回一个【迭代器】

        ---  3.X所有可迭代对象都实现了__iter__(self),之前有的实现__getitem__

验证法:

    from collections.abc import Iterable

    print(issubclass(dict, Iterable))

    

3. 迭代器iterator

    迭代器是迭代对象的特殊形式,即是迭代对象Iterable的子类

    迭代对象无法遍历,迭代器下有next方法才能遍历,因此调用迭代对象下的迭代器才能遍历

    

    迭代对象:不需要显式继承Iterable,只需要实现__iter__方法,返回迭代器

    迭代器: 不需要显式继承Iterable,只需要实现__iter__方法和__next__方法,返回迭代器

        (iter方法:    返回迭代器

        next方法:获得单个元素)

    > 如果通过next遍历元素,如果迭代器为空,返回StopIteration错误

    > 迭代器是一次性的,被next遍历后元素消失

    > for循环底层,每次都从迭代对象中,重新初始化一个迭代器

    eg:

    li = [1,2,4]

    # 获取迭代器

    ①   li.__iter__()       # 魔法方法不支持显示调用

    ② li.iter

    # 获得单个元素

    ①   li.__nex__t()

    ②   li.next

    print(next(li))

li = [1,2,4]
# 获取迭代器
①	li.__iter__()		# 魔法方法不支持显示调用
②  li.iter
# 获得单个元素
①	li.__nex__t()
②	li.next
print(next(li))
eg.# 模仿for循环底层

while True:
	it = iter(li)
	while True:
		try:
			item = next(it)
		except StopIteration:
			del it
			break

4. 自定义迭代类型

    满足抽象基础规范、

    迭代对象: 实现iter方法,返回迭代器

    迭代器 : next, iter方法返回自己

class My_Iterable:
	def __iter__(self):
		self.liter = [1,2,3,4]
		return li.__iter__()
		return My_Iteror(self.liter)
class My_Iteror:
	def __init__(self, li):
		self.liter = li
		self.index = 0
	def __iter__(self):
		return self
	def __next__(self):
		if self.index < len(self.liter)
			self.liter[self.index]
			self.index += 1
			return r
		else:
			raise StopIteration
m = My_Iterable()
it = iter(m)
print(next(it))

# 验证
print(isinstance(m, Iterable))
print(issubclass(My_Iterable, Iterable))

    迭代器 :只能用一次

    迭代对象:用来产生迭代器,可以狗仔复杂数据类型,提供给迭代器使用

    

5. 迭代器缺点:

    (1) 实现一个迭代器,需要iter,next方法

    (2) 不使用for循环,需要自己去捕获StopIteration异常

    (3) 一次性数据全部迭代,结果不一定全部使用,占用内存

二、 生成器

    --- 2.5后出现的可迭代对象,即底层使用了迭代器实现

1. 定义:

    懒加载(按需加载)的迭代器

2. 生成器的特征

    生成器不会一次性计算出存储的数据,而是一次只计算一个数据

3. 生成器的实现:

    > 生成器的数据获得方式:

        ① next

        ② 遍历

        

3.1 生成器表达式

    元组生成式 = 生成器

    x = (i**2 for in range(5))

    print(next(x))

3.2 生成器函数

    --- 带有yield关键字的函数

    yield: 能够让程序暂停到yeild位置,产出值

            注意:生成器函数调用时,不是直接执行,而是创建生成器对象,

    >   生成器可能包含多个yield

    >   生成器底层是迭代器

    >   可以通过next方法获得内部远胜于,也可以通过for遍历

    >   当函数终止时,再次获取元素,报StopIteration异常

    >   函数执行遇到yiled,程序暂停在该位置,返回对应值,将控制前交给调用者

def f():
	print('函数开始')
	for i in range(1, 101):
		print('yield前;')
		yield i		# 暂停,产出返回值i
		print('yield后:',i)
		return i
g = f()
next(g)
next(g)

3.3 yield表达式

    生成器:

        校核值 = yield 产出值

    调用端:

        产出值 = send(校核值)

    

    第一次调用生成器:产生生成器对象

    next:激活生成器

    

    > 生成器表达式的结果 是 调用生成器send方法传入的参数

    > 对象.send(None) == next(对象)

    > 激活生成器:① next()

                 ② send(None),必须是None

    > 获取生成器元素

        next(g)

        g.send('随意')

       

 def gen():

     for i in range(100):

         value = yield i**2      
         # value是send函数传入值,i**2为产出值,两者不会赋值,互不相干

         # value是send传入的参数,i是send或者next返回值

         print(f'value={value}')

    

    g = gen()               # 创建对象

    print(next(g))          # 激活生成器,传出0

    print(g.send('玩一玩'))    # value = '玩一玩,返回值为i

    

pl.需求,产生1 2 3 4 5,提示,输出5 4 3 2 1,....

   

pl.需求,产生1 2 3 4 5,提示,输出5 4 3 2 1,....
# 编写生成器,调整生成方案
import time
def gen():
	msg = ''
	while True:
		x = yield msg
		if x == 5:
			mag = 'too big'
		elif x == 1:
			msg = 'too small'
g = gen()

# 在主线插入生成器调用
while True:
	time.sleep(0.5)
	print(x)
	msg = g.send(x)
	
	if msg = 'too big':
		x -= 1
	else msg == 'too small'
		x += 1

        

三、 装饰器

> 装饰器的作用:

    (1) 扩展函数

    (2) AOP(面向切面):将共用模块提取出来,形参一个单独组件

1. 闭包

    --- 在内部函数中访问外部函数的变量;

        在外部函数中返回内部函数的函数名(调用)

    (1) 条件:

        ① 嵌套函数

        ② 内部函数能够访问外围变量

        ③ 外部函数return 内部函数名

    eg.

    def outer():

        x = 1

        sef inner():

            print(x)

        return inner

    a = outer()

    print(a.__closure__)

    (2) 适用情形

        ① 函数中有一些变量在被调用后仍然希望保留’

        ② 函数需要扩展功能,但是不能修改原函数的名字

	

# 原始
from datetime import datetime
def on_duty(name):
	print(f'{name}在上班')
def off_duty(name):
	print(f'{name}离开了')
	print(datetime.now())

# 闭包,不修改原始功能,添加新功能
def new(func):
	def inner(name):
		func(name)
		print('添加性能')
	return inner
on_duty = new(on_duty)
on_duty('Geoffrey')

2. 装饰器的作用:  

    > 用来处理被装饰的函数,返回函数的新名字,采用了闭包的思想

    > 在不修改原函数的基础上,对现有函数进行扩充、功能扩展

    

    (1) 格式

        在被调用函数上面加 @装饰名

            --- 一种语法糖

        原函数名 = 装饰函数(被装饰函数)

def new(func):
	def inner(name):
		func(name)
		print('添加性能')
	return inner

@new
def on_duty(name):
	print(f'{name}在上班')

3. 装饰器的优化

    (1) 参数的优化 --- 万能参数

    (2) 返回值的优化

        ---- 在inner中,提取func返回值作为inner返回值

    (3) 装饰器的叠加

        --- 使用装饰器扩展更多的功能,

from functools import wraps

def record(*args):
    def _inner(func):
        @wraps(func)                    # 保留原函数的元信息
        def __inner(*arg, **kwarg):     # 万能参数
            r = func(*arg, **kwarg)
            print(f'{args[0]}天后去{args[1]}向领导汇报工作')
            return r
        return __inner
    return _inner

# 测试嵌套装饰器
def new(func):                          # 为原函数添加的新功能
    def inner(*args, **kwargs):
        r = func(*args, **kwargs)       # 添加返回值
        print('new装饰器添加性能')
        return r                        # 此处对应原函数返回值
    return inner

@record(20, '上海')
@new
def on_duty(name):
    print(f'{name}在上班')

on_duty('Geoffrey')

4. 含有参数的装饰器

        --- 如果装饰器中需要使用其他参数,在外部做一个闭包

            在装饰原函数的时候,在装饰器后面加上(参数)

            不要在装饰器层传入参数,装饰器层只传入函数名    

 

5.  保留元信息 (函数注释)

    functools 下的 wraps实现元信息的保留

    在inner层上面添加:

        @wraps(func) --- 形式参数func

        

6. 类装饰器

    --- 实现装饰器,除了装饰器函数,还可以使用类方法

   

class New:
	def __init__(self, func):
		self.func = func
	#使对象能通过方法调用,即执行 对象()
	def __call__(self, *args, **kwargs):
		self.func(*args, **kwargs)
		print('随便添加其他函数')
		
@new
def play(a, b):
	from datetime import datetime
	print(datetime.now())

猜你喜欢

转载自blog.csdn.net/u010359398/article/details/81448027
今日推荐