叠加装饰器与迭代器

﹥>﹥吖頭↗ 提交于 2019-12-04 09:02:06

一、叠加装饰器

在同一个被装饰对象中,添加多个装饰器,并执行,模板:

1 模板:
2         @装饰器1
3         @装饰器2
4         ...
5         def 被装饰对象():
6             pass

注意:装饰器在调用被装饰对象时才会执行添加的功能

- 叠加装饰器:
  - 装饰的顺序: 由下到上装饰
  - 执行的顺序: 由上往下

注意: 无论inner中出现任何判断,最后都要返回“调用后的被装饰对象” func(*args, **kwargs)

# 需求: 为被装饰对象,添加统计时间 与 登录认证功能
import time

# 定义一个全局变量,表示用户是否验证通过
login_info = {
    'login': False
}

# 登录功能
def login():
    # 判断用户没有登录时,执行登录功能
    name = input("username:").strip()
    pwd = input('password:').strip()
    if name == 'abc' and pwd == '123':
        print('登录成功!')
        login_info['login'] = True
    else:
        print('登录失败!')

# 登录认证装饰器
def login_auth(func):
    def inner1(*args, **kwargs):
        """
        注意: 无论inner中出现任何判断,
        最后都要返回“调用后的被装饰对象” func(*args, **kwargs)
        """
        if login_info.get('login'):
            res = func(*args, **kwargs)
            return res
        else:
            login()
            return func(*args, **kwargs)
    return inner1

# 统计时间装饰器
def time_record(func):
    def inner2(*args, **kwargs):
        print('开始统计')
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
        return res
    return inner2


# 下载电影功能
"""
- 叠加装饰器:
        - 装饰的顺序: 由下到上装饰
        - 执行的顺序: 由上往下
"""
@time_record    # inner2 = time_record(inner1地址)
@login_auth     # inner1 = login_auth(func)
# @time_record
def func():
    print('start')
    time.sleep(2)
    print('end')

func()
# login()
# 执行的顺序: 先执行time_ record功能,再执行login_auth功能
# 统计登录时间 + 下载时间
# func()

# 装饰顺序
# @login_auth   # inner1 = login_auth(inner2)
# @time_record  # inner = time_record(download_movie)

# 执行顺序:
# 先执行login_auth, 再执行time_record
# 只统计下载电影的时间
# login()  # 先调用登录,模拟用户已登录

二、有参装饰器

# 无参装饰器: 装饰在被装饰对象时,没有传参数的装饰器。
'''
# 以下是无参装饰器
@wrapper1  # inner1 = wrapper1(inner2)
@wrapper2  # inner2 = wrapper2(inner3)
@wrapper3
'''

了解无参装饰器后,我们可以再实现一个用来为被装饰器对象添加认证功能的装饰器,需要传入用户级别参数

# 有参装饰器: 在某些时候,我们需要给用户的权限进行分类
'''
# 以下是有参装饰器
@wrapper1(参数1)  # inner1 = wrapper1(inner2)
@wrapper2(参数2)  # inner2 = wrapper2(inner3)
@wrapper3(参数3)
'''
# 需求,给登录功能再添加一个可以验证用户级别的功能,传入级别参数
def user_auth(user_level):      # 'VIP'
    def wrapper(func):
        def inner(*args, **kwargs):
            if user_level == 'VIP':
                print('VIP用户')
                res = func(*args, **kwargs)
                return res
            else:
                print('普通用户')
                return func(*args, **kwargs)
        return inner
    return wrapper

# 被装饰对象

# wrapper = user_auth('普通')
# @wrapper
# @user_auth('VIP')  # wrapper = user_auth('普通用户')
# @wrapper  #<--- 返回结果(wrapper) <---- user_auth()

# 有参装饰器的使用
@user_auth('VIP')
def func():
    pass

func()

有参装饰器,最外一层函数参数可以随意添加,所以装饰器对象最多包含三层

三、wraps

wraps是一个修复工具,修复的是被装饰对象的空间,使用方法

from functools import wraps

def wrapper(func):
    @wraps(func)    # 修改名称空间:inner ---> func
    def inner(*args, **kwargs):
        """
        此处是装饰器的注释
        :param args:
        :param kwargs:
        :return:
        """
        res = func(*args, **kwargs)
        return res
    return inner

@wrapper
def func():
    """
    此处是func函数的注释
    :return:
    """
    pass

# 函数对象.__doc__:查看函数内部的注释
print(func.__doc__)
# 结果就是返回func里面的注释

四、迭代器

迭代的工具

 

迭代:

  迭代指的是重复迭代,每一次迭代都是基于上一次的结果而来的

迭代器:

  迭代器指的是迭代取值的工具,它可以迭代取值

可迭代对象:

  凡是内部有str.__iter__()方法的都是可迭代对象。

  可迭代对象有:字符串str、列表list、元组tuple、字典dict、集合set、文件f

获取迭代器:

  通过可迭代对象.__iter__(), 得到的返回值就是 “迭代器对象”。

  迭代器是迭代取值的工具,作用是迭代取值

如何迭代取值:

  迭代对象.__next__()  每执行一次,都会从迭代器对象中取出一个值

str1 = '123456'
iter_str = str1.__iter__()
print(iter_str)     # iterator指的是迭代器对象
print(iter_str.__next__())
print(iter_str.__next__())
print(iter_str.__next__())
print(iter_str.__next__())
print(iter_str.__next__())
print(iter_str.__next__())

# 因为迭代器中的值已经取完
# print(iter_str.__next__())    # 报错StopIteration

- 总结: 可迭代对象 VS 迭代器对象:
  - 获取可迭代对象: 定义序列类型(str, list, set, tuple, dict, f)
    - 特点:
      内置有__iter__()

  - 获取迭代器对象: 通过可迭代对象调用.__iter__()得到返回值
    - 特点:
      内置有__next__()

- 迭代器对象的优点:
  - 优点:
    1.不依赖于索引迭代取值。
    2.节省内存空间。

  - 缺点:
    1.取指定某个值麻烦
    2.每次取值都要从第一个值开始,无法同过索引取值。

当迭代器中的值取完时,继续执行会报错,这里补充一个try语法防止此问题

list1 = [1,2,3,4]
iter_list = list1.__iter__()
while True:
    try:
        print(iter_list.__next__())     # 若不捕获异常,当取完迭代器对象后会报错
    # 当遇到StopIteration异常,会立即执行此处代码
    except StopIteration:
        break

for循环原理
  语法: for i in 可迭代对象:
  in: 可迭代对象 ----> 内部会自动调用.__iter__() ---> 迭代器对象
  for line in list1:
    # 迭代器对象.__next__()

# 不依赖于索引取值
list1 = [1,2,3,4]
for i in list1:     # list1是可迭代对象 ----> 内部会自动调用.__iter__() ---> 迭代器对象
    # 迭代器对象.__next__()
    print(i)
# 依赖于索引取值
list1 = [1,2,3,4]
n = 0
while n < len(list1):
    print(list1[n])
    n += 1

迭代器本质上是一个可迭代对象

文件本质上既是迭代器对象,也是可迭代对象。

# 唯独文件比较特殊: 因为文件从读取出来的时候就是一个迭代器对象
# f ---> 可迭代对象, 还是迭代器对象
# 文件既是可迭代对象,也是迭代器对象。
f = open('test.txt','r',encoding='utf-8')
iter_f = f.__iter__()
print(iter_f is f)      # True

可迭代对象不一定是迭代器对象

set1 = {1,2,3,4}

iter_set1 = set1.__iter__()
iter_set1.__next__()

# 迭代器对象也是一个可迭代对象
# 判断可迭代对象是否是迭代器对象
print(iter_set1.__iter__() is iter_set1)    # True
print(iter_set1 is set1)        # False
# 可迭代对象不一定是迭代器对象

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!