18---有参装饰器+迭代器+生成器

一 装饰器补充知识

1 定义函数和装饰器

# 装饰器:认证功能
def auth(func):
    def wrapper(*args,**kwargs):
        user = input('敢问姑娘芳名?❤')
        if user == 'tank':
            res = func(*args,**kwargs)
            return res
        else:
            print('你不是我的菜,😱')

    return wrapper


# 定义函数
@auth
def index(x,y):
    """精致的猪猪女孩"""
    print(x,y)
    return '小可爱,打起精神来❤'

res = index(1,2)
print(res)

2 出现的问题

问题就是只是将wrapper伪装成了index,但是如果去调用属性的时候,用户就会发现被装饰器装饰过的index已经不是原来的index
print(index.__name__)   # wrapper(*args,**kwargs)
print(help(index))  # 没有index的文档解释

3 解决方式

Ⅰ 手动修改属性:将wrapper函数的属性改成和index相同的
问题:被装饰对象的属性值很多,如果逐一添加很麻烦而且可能会漏掉
def auth(func):
    def wrapper(*args,**kwargs):
        user = input('敢问姑娘芳名?❤')
        if user == 'tank':
            res = func(*args,**kwargs)
        else:
            print('你不是我的菜,😱')
        return res
    wrapper.__name__ = 'index'
    wrapper.__doc__ = func.__doc__
    return wrapper


# 定义函数
@auth
def index(x,y):
    """精致的猪猪女孩"""
    print(x,y)
    return '小可爱,打起精神来❤'

res = index(1,2)
print(res)

# 看一下index的属性--名字属性
print(index.__name__)   # index(*args,**kwargs)
print(help(index))  # 有了原index函数的文档解释
Ⅱ 对Ⅰ进行优化:使用python解释器提供的装饰器对wrapper函数进行装饰
from functools import wraps
# 装饰器:认证功能
def auth(func):
    @wraps(func)  # 将func函数的各种属性都添加给wrapper,解释器自带的装饰器
    def wrapper(*args,**kwargs):
        user = input('敢问姑娘芳名?❤')
        if user == 'tank':
            res = func(*args,**kwargs)
        else:
            print('你不是我的菜,😱')
        return res
    # wrapper.__name__ = 'index'
    # wrapper.__doc__ = func.__doc__
    return wrapper


# 定义函数
@auth
def index(x,y):
    """精致的猪猪女孩"""
    print(x,y)
    return '小可爱,打起精神来❤'

res = index(1,2)
print(res)

# 看一下index的属性--名字属性
print(index.__name__)   # index
print(help(index))  # 有了原index函数的文档解释

二 有参装饰器

1 知识储备

def outer(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)

        return res
    return wrapper
@outer  # index = outer(index)
def index():
    print('敢问路在何方?')

# @outer语法糖,相当于index = outer(index)
# 由于语法糖@的限制,装饰器函数只能有一个参数,就是被装饰对象的内存地址
# 参数是被装饰对象的内存地址(变量名),装饰一个对象只能传一个参数,所以外层函数受到语法糖的牵制,只能传一个参数--被装饰对象的内存地址

# 总结
# 偷梁换柱之后
# index的参数什么样子,wrapper的参数就应该什么样子
# index的返回值什么样子,wrapper的返回值就应该什么样子
# index的属性什么样子,wrapper的属性就应该什么样子==》from functools import wraps

2 实现有参装饰器的思路

# 需求:认证装饰器,需要判断用户名密码是来自哪里?比如文件或者数据库
# 需求:具有认证功能的装饰器
# 1 登录才能执行index函数体代码
# 装饰器:为了避免语法繁琐,此时认为用户信息来自文件
def outer(func):
    def wrapper(*args,**kwargs):
        user = input('name').strip()
        pwd = input('pwd').strip()
        if user == 'egon' and pwd == '123':
             res = func(*args,**kwargs)
        else:
            print('登陆失败')
        return res
    return wrapper
# 被装饰对象
def index(x,y):
    print(x,y)

# 1 中存在的问题:用户的信息不一定来自文件,可能是来自数据库msql,或者是ldap
# 首先想到的解决思路:就是判断用户信息是从哪里来的
# 问题:db_type是一个变量,所以需要给db_type传参数
# 解决方式:通过outer函数给db_type传参
# 产生的新问题:不能使用语法糖
# 注意:语法糖的语法是:再被装饰对象的正上方,参数只能是被装饰对象的内存地址如index=outer(index),只能对一个函数进行装饰,传两个参数会报错,所以如果直接用outer传值的话就不能使用语法糖
def outer(func,db_type):
    def wrapper(*args,**kwargs):
        user = input('name').strip()
        pwd = input('pwd').strip()
        if db_type == 'file':
            print('我是基于文件登录的')
            if user == 'egon' and pwd == '123':
                 res = func(*args,**kwargs)
                 return res
            else:
                print('登陆失败')
        elif db_type == 'msql':
            print('我是基于数据库msql登录的')
        elif db_type == 'ldap':
            print('我是基于ldap登录的')
        else:
            print('我不支持此类型')

    return wrapper
# # 被装饰对象
# @outer  outer() missing 1 required positional argument: 'db_type'
def index(x,y):
    print(x,y)
index = outer(index,db_type='file')
index(1,2)

def f1(x,y):
    print(x,y)
f1 = outer(f1,db_type='msql')
f1(3,4)

# 2 优化解决方式1--继续使用语法糖
# 解决方式:继续使用语法糖就意味着不能通过outer给db_type传参,所以需要从外部传参给db_type---闭包函数
# 新出现的问题:需要在语法糖之前还需要给outer传参,太麻烦
def auth(db_type):
    # db_type= 'file'
    def outer(func):
        def wrapper(*args, **kwargs):
            user = input('name').strip()
            pwd = input('pwd').strip()
            if db_type == 'file':
                print('我是基于文件登录的')
                if user == 'egon' and pwd == '123':
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('登陆失败')
            elif db_type == 'msql':
                print('我是基于数据库msql登录的')
            elif db_type == 'ldap':
                print('我是基于ldap登录的')
            else:
                print('我不支持此类型')

        return wrapper

    return outer


outer = auth('file')


@outer
def index(x, y):
    print(x, y)


index(1, 2)
outer = auth('msql')


@outer
def f1(x, y):
    print(x, y)


f1(3, 4)


# 3 最终版
def auth(db_type):
    # db_type= 'file'
    def outer(func):
        def wrapper(*args, **kwargs):
            user = input('name').strip()
            pwd = input('pwd').strip()
            if db_type == 'file':
                print('我是基于文件登录的')
                if user == 'egon' and pwd == '123':
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('登陆失败')
            elif db_type == 'msql':
                print('我是基于数据库msql登录的')
            elif db_type == 'ldap':
                print('我是基于ldap登录的')
            else:
                print('我不支持此类型')

        return wrapper

    return outer


@auth('file')  # outer = auth('file')
def index(x, y):
    print(x, y)


index(1, 2)


@auth('msql')
def f1(x, y):
    print(x, y)

3 总结--有参装饰器的模板

# 有参装饰器模板
def 有参装饰器(x, y, z):
    def outter(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res

        return wrapper

    return outter


@有参装饰器(1, y=2, z=3)
def 被装饰对象():
    pass

三 迭代器

1 什么是迭代器

迭代取值的工具,迭代是一个重复的过程,每次重复都是基于上一次的结果而而继续的,单纯的重复不是迭代

2 为何要用迭代器

#   迭代器是用来迭代取值的工具,涉及到把多个数据取出的数据类型有列表/元组/字典/字符串/集合/文件对象
#   在不考虑for循环的前提下,以列表为例,用while循环取出列表中的每个值
list1 = [1,2,3,4]
count = 0
while count < len(list1):
    print(list1[count])
    count += 1
# 上述方法是根据索引来取值的,那么对于没有索引的数据类型比如字典,需要如何取值呢?
# 所以python解释器必须提供一种不依赖索引取值的方式----迭代器

3 如何使用迭代器

# Ⅰ 可迭代对象:但凡内置有__iter__方法的都称之为可迭代对象(可以转换成迭代器的对象)
'abc'.__iter__()
[1,2].__iter__()
(1,).__iter__()
{'a':1}.__iter__()
{1,2}.__iter__()
with open('a.txt','r',encoding='utf-8') as f:
    f.__iter__()

# Ⅱ 将可迭代对象转换成迭代器__iter__方法
dic = {'a':1,'b':2,'c':3}
dic_iteration = dic.__iter__()
print(dic_iteration)  # <dict_keyiterator object at 0x000001F682CFD180>
# 迭代器有一个内置方法__next__,将迭代器中的值取出
print(dic_iteration.__next__())
print(dic_iteration.__next__())
print(dic_iteration.__next__())
# 当迭代器中的值全部取出之后,继续输出迭代器的内容,会抛出异常  StopIteration
# print(dic_iteration.__next__())
# 迭代器可以理解为一只会下蛋的母鸡,假设这只母鸡(字典的迭代器)一辈子只能下3个鸡蛋(字典中的值),那么下完这三个蛋之后,母鸡就会死掉,不能再继续下单

# 可以用循环实现上面重复的输出:异常处理,当出现异常的时候会终止循环,不会报错
while True:
    try:
        print(dic_iteration.__next__())
    except StopIteration:
        break
# 问题:会发现用print输出迭代器的内容之后,用while循环实现的时候,已经无法取到迭代器的值了
# 相当于迭代器这只母鸡下完蛋之后就死掉了,再让它下蛋你觉得可能吗?
# 解决办法:再造一只母鸡即可
dic_iteration = dic.__iter__()
while True:
    try:
        print(dic_iteration.__next__())
    except StopIteration:
        break

# 将列表用迭代器的方式输出值
list2 = [1,2,3,4]
# 1 将列表这个可迭代对象转换成迭代器
list2_iteration = list2.__iter__()
# 2 通过迭代器的内置方法__next__,用while循环取出迭代器中的每个值
while True:
    try:
        print(list2_iteration.__next__())
    except StopIteration:
        break
# 连续两次执行while循环,第二次也是取不到值的。
while True:
    try:
        print(list2_iteration.__next__())
    except StopIteration:
        break

4 迭代器和可迭代对象详解

可迭代对象:可以转换成迭代器的对象,只要内置方法中有__iter__,就是可迭代对象
迭代器对象:内置方法中有__next__方法并且有__iter__方法的对象
           迭代器对象.__next__:得到迭代器对象的下一个值
           迭代器对象__iter__:得到的还是原本的迭代器,说白了,就是使用迭代器调用__iter__方法时和没调用这个方法是一样的
                             是为了使用for循环时统一格式,文件对象就是可迭代对象也是迭代器
# 举例说明
dic = {'a':1,'b':2}
dic_iteration = dic.__iter__()
print(dic_iteration is dic_iteration.__iter__().__iter__())  # True

# 可迭代对象:字符串 列表 元组 字典 集合 文件对象
# 迭代器对象:文件对象


# for循环的工作原理:for循环可以称之为迭代器循环
d={'a':1,'b':2,'c':3}
# 1 可迭代对象内置方法__iter__方法将字典转换为迭代器
d_iteration = d.__iter__()
# 2 调用迭代器内置方法__next__取迭代器中的下一个值
while True:
    try:
        print(d_iteration.__next__())
    except StopIteration:
        break
# 3 循环步骤2,直到for循环捕捉到异常,终止循环

5 迭代器的优缺点

5.1 优点:
I、为序列和非序列类型提供了一种统一的迭代取值方式。
II、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。

5.2 缺点:
I、除非取尽,否则无法获取迭代器的长度
II、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。

四 生成器

# 需求:造一个数据类型,可以产生很多很多的数字
# 如何得到自定义的迭代器
# 在函数内一旦存在yield关键字,再调用函数才是生成器,调用函数的时候并不会执行函数体代码,会返回一个生成器对象,就是自定义的迭代器

# 用函数返回无穷个值
def func():
    print('第一次')
    yield 1
    print('第二次')
    yield 2
    print('第三次')
    yield 3
    print('第四次')


g = func()   # <generator object func at 0x000002085D000200>
print(g)
# 生成器就是迭代器
# g.__iter__()
# g.__next__()
# __next__会触发函数体代码的运行,然后遇到yield停下来,将yield后的值当作本次调用的结果
res1 = (g.__next__())
print(res1)
res2 = g.__next__()
print(res2)

while True:
    try:
        print(g.__next__())
    except StopIteration:
        break


res = 'aaa'
len(res) # res.__len__()
iter(res) == res.__iter__()

# 生成器应用案例:生成1,3,5,7.。。。

def my_range(start,stop,step):
    while start < stop:

        yield start

        start += step

res = my_range(1,10,2)
# 此时res是一个自定义的迭代器,不会执行函数体代码
while True:
    try:
        print(next(res))
    except StopIteration:
        break

for i in range(1,10,2):
    print(i)
posted @ 2020-03-24 19:45  微信搜索-程序媛小庄  阅读(142)  评论(0编辑  收藏  举报