zoukankan      html  css  js  c++  java
  • 第十三章 迭代器、生成器、 装饰器

    一、可迭代对象

    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())
  • 相关阅读:
    Linux下定时删除指定目下n天前的文件
    日期时间格式化
    sed与awk
    Linux守护进程(init.d和xinetd)
    python-Json模块
    python3 urllib模块
    linux 命令 rsync
    Linux下scp的用法
    代码块重定向
    使用exec
  • 原文地址:https://www.cnblogs.com/geoffreyone/p/9899760.html
Copyright © 2011-2022 走看看