装饰器——迭代器——生成器

目录

一.装饰器

二.迭代器

三.生成器

 

装饰器

一、装饰器介绍

1.1 什么是装饰器

  • 器指的是工具,可以定义成函数
  • 装饰:为其他事物添加额外的东西点缀
  • 装饰器指的是定义一个函数,该函数是用来为其他函数添加额外的功能

1.2 为什么要用装饰器

  • 开放封闭原则:对扩展开放,对修改封闭(对扩展功能开放,对修改源代码封闭)
  • 装饰器就是在不修改被装饰器对象的源代码以及调用方式的前提下为被装饰对象添加新功能

二、装饰器的实现

2.1 无参装饰器的实现

# 需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
'''
import time
def index(x,y):
    time.sleep(3)
    print('index %s, %s' % (x,y))
index(111,222)
'''
# 解决方案一:失败
# 问题:没有修改被装饰对象的调用方式,但是修改了其源代码
import time
def index(x,y):
    start=time.time()
    time.sleep(3)
    print('index %s %s' %(x,y))
    stop = time.time()
    print(stop - start)

index(111,222)

# 解决方案二:
# 问题:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能
#      但是代码冗余
import time
def index(x,y):
    time.sleep(3)
    print('index %s %s' %(x,y))

start=time.time()
index(111,222)
stop=time.time()
print(stop - start)

start=time.time()
index(111,222)
stop=time.time()
print(stop - start)

# 解决方案三:
# 问题:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了
import time
def index(x,y):
    time.sleep(3)
    print('index %s %s' %(x,y))

def wrapper():
    start=time.time()
    index(111,222)
    stop=time.time()
    print(stop - start)

wrapper()

# 方案三的优化
import time
def index(x,y):
    time.sleep(3)
    print('index %s %s' %(x,y))

def wrapper(*args,**kwargs):
    start=time.time()
    index(*args,**kwargs)
    stop=time.time()
    print(stop - start)

wrapper(111,222)

# 解决方案四
# 问题:解决了方案三函数的调用方式改变问题,但带来一个新问题即被装饰对象有返回值时返回值为None
import time
def run_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
    return wrapper

def index(x,y):
    time.sleep(1)
    print('index %s,%s' % (x,y))
    return 123
res = index(111, 222)
print(res)

# 方案四优化(装饰器终极版):给func用res来接收返回值然后return res,来实现对被装饰函数返回值的接收
import time
def run_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res
    return wrapper

def index(x,y):
    time.sleep(1)
    print('index %s,%s' % (x,y))
    return 123
index = run_time(index)
res = index(111, 222)
print(res)

2.2 语法糖:让你开心的语法

import time
def run_time(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res
    return wrapper

# 在被装饰对象正上方的单独一行写@装饰器名字
@run_time # index = run_time(index)
def index(x,y):
    time.sleep(1)
    print('index %s,%s' % (x,y))
    return 123
res = index(111, 222)
print(res)

# 补充:wraps的使用
from functools import wraps
# 无参装饰器模板
def outter(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        # 1、调用原函数
        # 2、为其增加新功能
        res=func(*args,**kwargs)
        return res
    # wrapper.__name__ = func.__name__
    # wrapper.__doc__ = func.__doc__
    return wrapper

def index():
    """这个是主页功能"""
    print('welcome index')

print(index.__name__)
print(index.__doc__)

2.3 有参装饰器的实现

def auth(db_type='file'):
    def deco(func):
        def wrapper(*args,**kwargs):
            name = input("your name:").strip()
            pwd = input("your password:").strip()
            if db_type == 'file':
                print("基于文件的验证")
                if name == 'umi' and pwd == '123':
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('user or password error')
            elif db_type == 'mysql':
                print('基于mysql的验证')
            elif db_type == 'ldap':
                print('基于ldap的验证')
            else:
                print('不支持该db_type')
        return wrapper
    return deco

@auth(db_type='file')
def index():
    print('welcome index')

@auth(db_type='mysql')
def home():
    print('welcome home')

@auth(db_type='ldap')
def refier():
    print('welcome index')

index()
home()
refier()

2.4 总结

# 无参装饰器模板
def outter(func):
    def wrapper(*args,**kwargs):
        # 1、调用原函数
        # 2、为其增加新功能
        res=func(*args,**kwargs)
        return res
    return wrapper
def index():
    print('welcome index')
index()

# 偷梁换柱:即将原函数名指向的内存地址偷梁换柱成wrapper函数
#        所以应该将wrapper做的跟原函数一样才行

# 有参装饰器模板
def 有参装饰器(x,y,z):
    def outter(func):
        def wrapper(*args,**kwargs):
            # 1、调用原函数
            # 2、为其增加新功能
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter

@有参装饰器(1,y=2,z=3)
def index():
    print('welcome index')
index()




迭代器

一、迭代器介绍

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

    # 单纯的循环不是迭代
    while True:
        name = input()
    
    # 重复且迭代(迭代取值)
    l=['umi','rem','ram']
    i=0
    while i < len(l):
        print(l[i])
        i+=1
        
    # 迭代器设计用来取值的工具,而涉及到把多个值循环取出来的类型
    # 有:列表、字符串、元组、字典、集合、文件
    
    # 上述迭代取值的方式只适用于有索引的数据类型(有序类型):列表、字符串、元组
    # 为了解决索引迭代取值的局限性,python必须提供一种不依赖于索引的取值方式,这就是迭代器

                                  

  • 1.1 可迭代对象

  • 但凡内置有 __iter__ 方法的都称之为可迭代对象

    s1=''
    # s1.__iter__()
    l=[]
    # l.__iter__()
    t=(1,)
    # t.__iter__()
    d={'a':1}
    # d.__iter__()
    set1={1,2,3}
    # set1.__iter__()
    with open('a.txt',mode='r') as f:
        # f.__iter__()
        pass
    # 调用可迭代对象下的__iter__方法会将其转换成迭代器对象
    d={'a':1,'b':2,'c':3}
    d_iterator = d.__iter__()
    # print(d_iterator)
    '''
    print(d_iterator.__next__())
    print(d_iterator.__next__())
    print(d_iterator.__next__())
    print(d_iterator.__next__()) # 抛出异常StopIteration表示取值去干净了
    '''
    while True:
        try:
            print(d_iterator.__next__())
        except StopIteration:
            break
    print('=======》》》') # 在一个迭代器取值取完的情况下,在对其取值取不到
    d_iterator = d.__iter__()
    while True:
        while True:
        try:
            print(d_iterator.__next__())
        except StopIteration:
            break
            
    # 对有索引类型进行迭代取值
    l=[1,2,3,4,5]
    l_iterator=l.__iter__()
    
    while True:
        try:
            print(l_iterator.__next__())
        except StopIteration:
            break

 

1.2迭代器对象
  • 调用可迭代对象下的 __iter__方法会将其转换成迭代器对象
  • 通过 __next__ 方法来获取迭代器对象的值
  • 当迭代器对象中的值结束,抛出 StopIteration 的异常,代表无值可取,迭代结束

1.3 可迭代对象与迭代器对象详解

  • 1.3.1 可迭代对象("可以转换成迭代器的对象"):内置有 __iter__ 方法的对象

    # 可迭代对象.__iter__():得到迭代器对象

    1.3.2 迭代器对象:内置有 __next__ 方法并且内置有 __iter__ 方法的对象

  • # 迭代器对象.__next__(): 得到迭代器的下一个值
    # 迭代器对象.__iter__(): 得到迭代器本身,简单来说就是调了没调一个样
    dic={'a':1,'b':2,'c':3}
    
    dic_iterator=dic.__iter__()
    print(dic_iterator is dic_iterator.__iter__().__iter__().__iter__())
    
    # 可迭代对象:字符串、列表、元组、字典、集合
    # 迭代器对象:文件
    '123'.__iter__()
    
    [1,2,3].__iter__()
    
    (1,2,3).__iter__()
    
    {'a':1}.__iter__()
    
    {1,2,3}.__iter__()
    
    with open('a.txt',mode='w') as f:
        f.__iter__()
        f.__next__()

二、for循环原理

  • for循环可以称之为叫迭代器循环
d={'a':1,'b':2,'c':3}

'''
1、d.__iter__()得到一个迭代器对象
2、迭代器对象.__next__()拿到一个返回值,然后将该返回值赋值给k
3、循环步骤2,直到抛出StopIteration异常for循环会捕捉异常然后结束循环
'''
for k in d:
    print(k)
    
with open('a.txt',mode='rt',encoding='utf-8') as f:
    for line in f: # f.__iter__()
        print(line)


list('hello') #原理同for循环

三、迭代器的优缺点

3.1 优点

  • 3.1.1 为序列和非序列类型提供了一种统一的迭代取值方式。

  • 3.1.2 惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。

3.2 缺点

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



生成器

一、生成器与yield

# 如何得到自定义的迭代器:
# 在函数内一旦存在yield关键字,调用函数并不会执行函数体代码
# 会返回一个生成器对象,生成器
def func():
    print('第一次')
    yield 1
    print('第二次')
    yield 2
    print('第三次')
g = func()
print(g) # generator
# 生成器就是迭代器
g.__iter__()
g.__next__()
# 会触发函数体代码的运行,然后遇到yield停下来,将yield后的值当作本次调用的结果返回
res1 = g.__next__()
print(res1)
# 应用案例
# 需求:造一个能够产生无穷个值的数据类型
1 def my_range(start, stop, step=1):
2     # print('start...')
3     while start < stop:
4         start += step
5         yield start
6     # print('end...')
7 
8 for n in my_range(1, 7, 2):
9     print(n)
   
# yield与return对比总结说明:
函数调用函数体代码执行到yield时会返回yield后的值并将函数运行状态暂停在yield的位置
而return,当函数被调用时函数体代码运行到return时返回return后的值但是函数直接被结束掉了。
因此,return只能返回一次值,而yield可以返回多次值

 

二、yield表达式应用

 

三、三元表达式、列表生成式、生成器表达式

3.1 三元表达式

3.2 列表生成式

3.3 生成器表达式



原文地址:https://www.cnblogs.com/2722127842qq-123/p/12559499.html