1装饰器

1.1装饰器介绍

1.1.1什么是装饰器

’装饰代指为被装饰对象添加新的功能,’器’代指器具/工具

装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

提示:可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。

1.1.2为什么要用装饰器

装饰器的核心思想:(开放封闭原则)

在不修改被装饰者源代码以及调用方式的前提下,为被装饰者添加新功能

软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。

软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

1.2装饰器使用

函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。

如何实现装饰器:可以用闭包函数去实现装饰器

1.2.1无参装饰器

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def foo():
    time.sleep(3)
    print('from foo')
foo()
无参装饰器

1.2.2有参装饰器

def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')

有参装饰器

1.3装饰器模板

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

1.4叠加多个装饰器

11.装饰器和迭代器_内存地址

def deco1(func1):  # func1=函数wrapper2的内存地址
    def wrapper1(*args,**kwargs):
        print('=========>wrapper1')
        res1 = func1(*args,**kwargs)
        return res1
    return wrapper1

def deco2(func2):  # func2=函数wrapper3的内存地址
    def wrapper2(*args,**kwargs):
        print('=========>wrapper2')
        res2 = func2(*args,**kwargs)
        return res2
    return wrapper2

def deco3(func3):  # func3 = 最原始那个index函数的内存地址
    def wrapper3(*args,**kwargs):
        print('=========>wrapper3')
        res3 = func3(*args,**kwargs)
        return res3
    return wrapper3

        # index=函数wrapper1的内存地址

@deco1  # deco1(函数wrapper2的内存地址)->函数wrapper1的内存地址
@deco2  # deco2(函数wrapper3的内存地址)->函数wrapper2的内存地址
@deco3  # deco3(最原始那个index函数的内存地址)->函数wrapper3的内存地址
def index():
    print('=------------>index')
    return 123

res = index()
print(res)

运行结果:

11.装饰器和迭代器_for循环_02

2.迭代器

2.1.迭代器介绍

2.1.1什么是迭代器

迭代是一个重复的过程,但是每一次重复都是基于上一次结果而继续的

迭代器指的是迭代取值的工具

2.1.2为什么用迭代器

(1)为了找到一种统一迭代取值方案(适用于str、list、tuple、dict、set,文件对象)
(2)节省内存

2.2.迭代器使用

2.2.1如何用迭代器

可迭代的对象iterable: 内置有__iter__方法的对象(str、list、tuple、dict、set,文件对象)

   迭代器对象iterator: 内置有__iter__方法,内置有__next__方法
调用obj.**iter**()方法返回的结果就是一个迭代器对象(Iterator)。迭代器对象是内置有**iter**和**next**方法的对象,
打开的文件本身就是一个迭代器对象,执行迭代器对象.**iter**()方法得到的仍然是迭代器本身,
而执行迭代器.**next**()方法就会计算出迭代器中的下一个值。 迭代器是Python提供的一种统一的、
不依赖于索引的迭代取值方式,只要存在多个“值”,无论序列类型还是非序列类型都可以按照迭代器的方式取值
>>> s={1,2,3} # 可迭代对象s
>>> i=iter(s)  # 本质就是在调用s.__iter__(),返回s的迭代器对象i,
>>> next(i) # 本质就是在调用i.__next__()
1
>>> next(i)
2
>>> next(i)
3
>>> next(i)  #抛出StopIteration的异常,代表无值可取,迭代结束

2.3. for循环原理

有了迭代器后,我们便可以不依赖索引迭代取值了,使用while循环的实现方式如下

goods=['mac','lenovo','acer','dell','sony']
i=iter(goods) #每次都需要重新获取一个迭代器对象
while True:
    try:
        print(next(i))
    except StopIteration: #捕捉异常终止循环
        break

for循环又称为迭代循环,in后可以跟任意可迭代对象,上述while循环可以简写为

goods=['mac','lenovo','acer','dell','sony']
for item in goods:   
    print(item)

for 循环在工作时,首先会调用可迭代对象goods内置的iter方法拿到一个迭代器对象,然后再调用该迭代器对象的next方法将取到的值赋给item,执行循环体完成一次循环,周而复始,直到捕捉StopIteration异常,结束迭代。

2.4.迭代器的优缺点

基于索引的迭代取值,所有迭代的状态都保存在了索引中,而基于迭代器实现迭代的方式不再需要索引,所有迭代的状态就保存在迭代器中,然而这种处理方式优点与缺点并存:

2.4.1 优点:

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

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

2.4.2 缺点:

1、除非取尽,否则无法获取迭代器的长度

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