Python day14:闭包函数与装饰器

一、闭包函数
闭包函数=函数嵌套定义+函数对象+名称空间与作用域

1、闭:指的是该函数是定义在一个函数内部的函数
2、包:值得是该函数访问了一个来自于外层函数的变量

为函数体传参的方法:

'''方案一:直接使用参数的形式传递'''
def wrapper(x):
    print(x)

wrapper(111)
wrapper(222)
'''方案二:把函数体想要的参数包给它(即使用闭包的概念)'''
def outter(x):
    x = 111

    def wrapper():  # wrapper = 闭包函数的内存地址
        print(x)

    return wrapper  # 一定不要加括号
    return 闭包函数的内存地址

f1 = outter(111)  # f = 闭包函数的内存地址
f2 = outter(222)  # f = 闭包函数的内存地址

乍一看会感觉使用闭包来传参数非常的麻烦,我们之前使用函数,需要参数都是直接传给他,方便也快捷,但是在某些场景下我们定死了某个函数无法直接传参数,那就必须通过其他方式传参数,即闭包的方式,下面介绍的装饰器就是闭包的使用

二、无参装饰器
1、什么是装饰器
器:工具
装饰:为被装饰者添加额外的功能

2、为何要有装饰器
软件一旦上线运行之后,就应该遵循开放封闭原则:
1、开放指的是对拓展新功能开放
2、封闭指的是对修改源代码封闭
这种情况下,我们在写新功能时,若需要用到新的参数,就无法直接通过原函数来传了,必须通过其他方式来传参,目前我们想到的方法,是两种传参的另一种的方式,闭包函数

定义装饰器的目的:
定义装饰器就是为了在遵循1和2的前提下来为其他函数添加新功能的

ps:
不修改被装饰对象指的是定义与调用都不能修改
所以下述行为都违反了开放封闭原则:
①、修改被装饰对象定义时的源代码
②、修改被装饰对象的调用方式

3、如何用装饰器:

'''无参装饰器基本格式'''
def inner(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return func
    return wrapper()

4、无参装饰器的应用场景以及构建装饰器步骤
需求:有一个index函数,此时我们要在index函数基础上添加一个计算运算时间的功能,因为是公司项目,我们不能修改原函数的调用方式,也不能修改源代码。

一、被装饰器对象index如下
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

index(1,2)

二、为index添加计算运算时间功能
方案1问题:修改了源代码
import time

def index(x,y):
    start = time.time()
    time.sleep(1)
    print("index---->",x,y)
    end = time.time()
    print(end-start)

index(1,2)

方案2问题:需要找到所有调用index的位置
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)


start = time.time()
index(1,2)
end = time.time()
print(end-start)

方案3问题:函数被写死了只能算index的时间
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

def wrapper():
    start = time.time()
    index(1,2)
    end = time.time()
    print(end-start)

wrapper()

方案4:直接传index进去,改变了index的调用方式,现在需要用wrapper来调用index,我们需要再想一种传参的方式
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

def wrapper(func):
    start = time.time()
    func(1,2)
    end = time.time()
    print(end-start)

wrapper(index)

方案5问题:此时利用闭包函数,将index包给了wrapper,再重命名为index,我们在调用index时其实是调用wrapper,但有出现了问题,\
原函数在调用的时候需要传参数如index(1,2),而wrapper接收不了参数

import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

def outter(func):
    # func = index()
    def wrapper():
        start = time.time()
        func(1,2)
        end = time.time()
        print(end-start)
    return wrapper

index = outter(index)
index()

方案6
import time
from functools import wraps

def timmer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(end-start)
        return res
    return wrapper

def login(func):
    def wrapper(*args,**kwargs):
        name = 'yang'
        pwd = '123'
        inp_name = input("请输入您的用户名:").strip()
        inp_pwd = input("请输入您的密码").strip()
        if inp_name == name and inp_pwd == pwd:
            print("登录成功")
            res = func(*args,**kwargs)
            return res
    return wrapper

@timmer
@login
def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

index(1,2)

三、有参装饰器
对于不再需要新参数的装饰器,两层就可以解决了,但是当我们的装饰器需要新的参数,如在登录的时候,我们要判断我们用户名与密码的来源,此时需要外部将来源传进来。而两层的装饰器中,第一层,是为了给原函数传参数,他的参数不能动,而第二层,因为装饰器语法@的原因,也已经定死了此处只能传一个函数名,也不能传参数,所以我们需要构造第三层来接受外部的参数,而在内部的函数可以在不改动自己依然可以获取到外层的函数.

'''有参装饰器模版'''
def outter2(x,y,z,a,b):
    def outter1(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter1

def outter(engine = 'file'):
    def deco2(func2):
        def wrapper2(*args,**kwargs):
            inp_name = input('username>>>: ').strip()
            inp_pwd = input('password>>>: ').strip()

            if engine == "file":
                print('基于file的认证')
                if inp_name == "egon" and inp_pwd == "123":
                    print('login successful')
                    res2=func2(*args,**kwargs)
                    return res2
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基于mysql的认证')
            elif engine == 'ldap':
                print('基于ldap的认证')
            else:
                print('未知的engine')
        return wrapper2
    return deco2

@outter(engine='mysql')  # @deco2 # index=deco2(index)
def index(x,y):
    print('index=>',x,y)

index(1,2)  # index=>wrapper

四、叠加多个装饰器的运行步骤(记住结论既可)
2.1 加载顺序:自下而上
2.2 执行顺序:自上而下运行内层的wrapper函数
结论:

1、无参装饰器的模板
def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper


2、叠加多个装饰器
**2.1 加载顺序:自下而上**
**2.2 执行顺序:自上而下运行内层的wrapper函数**

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 = 最原始的那个被装饰函数的内存地址
    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(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址  
def index(x,y):
    print('index=>',x,y)

index(1,2)

"""
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2
"""


3 案例
import time

def deco1(func1):
    def wrapper1(*args,**kwargs):
        start_time = time.time()
        res1=func1(*args,**kwargs)
        stop_time = time.time()
        print(stop_time - start_time)
        return res1
    return wrapper1


def deco2(func2):
    def wrapper2(*args,**kwargs):
        inp_name = input('username>>>: ').strip()
        inp_pwd = input('password>>>: ').strip()
        if inp_name == "egon" and inp_pwd == "123":
            print('login successful')
            res2=func2(*args,**kwargs)
            return res2
        else:
            print('username or password error')
    return wrapper2

@deco2
@deco1
def index(x,y):
    time.sleep(1)
    print('index=>',x,y)

index(1,2)

你可能感兴趣的:(Python day14:闭包函数与装饰器)