7.11函数(三)

一、闭包函数

1.什么是闭包函数:

(1)闭:定义在函数内部的函数

(2)包:内部函数引用了外部函数作用域的名字

2.给函数传值的两种方式

(1)传参

def index(name):
    print(name)
index('francis')  # 传参

(2)闭包

def outter(x,y):
    # x = 1 通过传参获得
    # y = 40 通过传参获得
    def my_max():
        if x > y:
            return x
        return y
    return my_max
res = outter(1,40)  # res就是my_max函数的内存地址
print(res())  # 调用函数my_max,会去函数outter中取参数,这就是内部函数引用了外部函数作用域的名字

二、装饰器

1.什么是装饰器:

装饰:给被装饰对象添加新的功能

器:就是一个工具

2.为什么要用装饰器

(1)装饰器遵循开放封闭的原则

开放:对扩展开放(能对原来的函数添加功能)

封闭:对修改封闭(不能修改原来的函数功能)

(2)装饰器(是可调用对象)必须遵循的两个原则

  1.不改变被装饰对象(原函数)的源代码

  2.不改变被装饰对象(原函数)的调用方式

3.补充知识:

import time  # 调用模块
print(time.time())  # 打印时间戳,打印的是当前时间距离1970-1-1 00:00:00相差的秒数(1970-1-1 00:00:00是Unix诞生元年)
time.sleep(3)  # 让cpu睡三秒,传参数,表示让你的程序暂停几秒
print('程序停了3秒')

三、装饰器简单版本

1.用函数实现让程序停3秒,再执行打印功能

import time
def index():
    time.sleep(3)
    print('程序停了3秒')
index()

2.如何实现:

统计执行index函数(上面的)共用了几秒,且不改变原函数的调用方式和源代码。

import time
def index():
    time.sleep(3)
    print('程序停了3秒')

def outter(func):  # func = index  就是最原始的index函数的内存地址
    def get_time():
        start = time.time()  # 统计时间戳
        func()  # func = index  加括号调用最原始的index函数(让程序停3秒的函数)
        end = time.time()  # 统计时间戳
        print('index运行了:%s'%(end-start))  # 两次统计时间戳,用了几秒
    return get_time  # 返回get_time函数的内存地址
index = outter(index)  # outter(index)执行之后返回的是get_time函数的内存地址
# 所以第一个index指向get_time函数的内存地址,第二个index是最原始的index函数内存地址(让程序停3秒的函数)
index()  # 就相当于get_time函数加上括号调用了

四、装饰器升级版

1.如何实现:

用一个装饰器装饰两个函数,其中一个不用传参,一个需要传参,且该装饰器的返回值就是原函数的返回值,原函数:

import time
def index():
    time.sleep(3)
    print('程序停了3秒')
    return 'index的返回值'
res = index()
print(res)

def login(name):
    time.sleep(1)
    print('%s是帅比'%name)
    return 'login的返回值'
res1 = login('francis')
print(res1)

2.实现:

import time
def index():
    time.sleep(3)
    print('程序停了3秒')
    return 'index的返回值'

def login(name):
    time.sleep(1)
    print('%s是帅比'%name)
    return 'login的返回值'

def outter(func):  # func = 最原始的index或者login函数的内存地址
    def get_time(*args, **kwargs):  # *和**在形参中的用法,接收多余的位置参数和关键字参数
        start = time.time()
        res = func(*args, **kwargs)  # 最原始的index或者login函数的内存地址,加括号调用,并接收原函数的返回值
        # *和**在实参中的用法,打散容器类型中的参数和字典中的参数
        end = time.time()
        print('%s函数运行了:%s'%(func,end-start))
        return res  # 返回原函数的返回值
    return get_time
index = outter(index)  # 第二个index是最原始的index函数的内存地址
res = index()
print(res)  # 原index函数的返回值

login = outter(login)  # 第二个login是最原始的login函数的内存地址
res1 = login('francis')
print(res1)  # 原login函数的返回值
View Code

实现了无参函数和有参函数都可以直接调用同一个装饰器(装饰器可以接收任意数量的参数)

五、装饰器语法糖

1.上述实现的装饰器中,如果想要装饰某个函数,需要代码比如:index = outter(index)、login = outter(login)来实现,很麻烦,这时候可以用到装饰器语法糖。

import time

def outter(func):  # func = 最原始的index或者login函数的内存地址
    def get_time(*args, **kwargs):  # *和**在形参中的用法,接收多余的位置参数和关键字参数
        start = time.time()
        res = func(*args, **kwargs)  # 最原始的index或者login函数的内存地址,加括号调用,并接收原函数的返回值
        # *和**在实参中的用法,打散容器类型中的参数和字典中的参数
        end = time.time()
        print('%s函数运行了:%s'%(func,end-start))
        return res  # 返回原函数的返回值
    return get_time

@outter  # index = outter(index)
def index():
    time.sleep(3)
    print('程序停了3秒')
    return 'index的返回值'

@outter  # login = outter(login)
def login(name):
    time.sleep(1)
    print('%s是帅比'%name)
    return 'login的返回值'

res = index()
print(res)  # 原index函数的返回值
res1 = login('francis')
print(res1)  # 原login函数的返回值
View Code

@outter会将紧挨着他的可调用对象当作他的参数传入直接执行,并接收返回值

2.书写规范:

语法糖在书写的时候应该与被装饰对象紧紧挨着,两者之间不要有空格

六、装饰器模板

def outter(func):
    def inner(*args,**kwargs):
        print('执行被装饰函数之前,你可以做的操作')
        res = func(*args,**kwargs)
        print('执行被装饰函数之后,你可以做的操作')
        return res
    return inner

1.如何实现一个认证装饰器:

执行函数index之前必须先输入用户名和密码,正确之后才能执行index,如果之后再执行函数login,则不需要再登录

import time
user_dic = {'is_login':None}  # 全局定义一个空字典,判断用户是否已登录过
def outter(func):
    def inner(*args,**kwargs):
        if user_dic['is_login']:  # 判断是否已登录
            res = func(*args, **kwargs)  # 登陆过直接执行原函数
            return res
        else:
            username = input('please input your username>>>:').strip()
            password = input('please input your password>>>:').strip()
            if username == 'francis' and password == '123':
                user_dic['is_login'] = True  # 登陆成功后,修改字典为True
                res = func(*args,**kwargs)
                return res
            else:
                print('username or password error')
    return inner

@outter  # index=outter(index)
def index(name):
    time.sleep(1)
    print('%s是帅比'%name)
    return 666

@outter  # login=outter(login)
def login():
    time.sleep(1)
    print('from login')
    return 999

print(index('francis'))
print(login())
View Code

七、多层装饰器

1.给index函数套多个装饰器,使index函数先执行认证功能再统计函数运行时间

import time
user_dic = {'is_login':None}  # 全局定义一个空字典,判断用户是否已登录过
def outter(func):
    def inner(*args,**kwargs):
        if user_dic['is_login']:  # 判断是否已登录
            res = func(*args, **kwargs)  # 登陆过直接执行原函数
            return res
        else:
            username = input('please input your username>>>:').strip()
            password = input('please input your password>>>:').strip()
            if username == 'francis' and password == '123':
                user_dic['is_login'] = True  # 登陆成功后,修改字典为True
                res = func(*args,**kwargs)
                return res
            else:
                print('username or password error')
    return inner

def outter1(func):  # func = 最原始的index或者login函数的内存地址
    def get_time(*args, **kwargs):  # *和**在形参中的用法,接收多余的位置参数和关键字参数
        start = time.time()
        res = func(*args, **kwargs)  # 最原始的index或者login函数的内存地址,加括号调用,并接收原函数的返回值
        # *和**在实参中的用法,打散容器类型中的参数和字典中的参数
        end = time.time()
        print('%s函数运行了:%s'%(func,end-start))
        return res  # 返回原函数的返回值
    return get_time

@outter  # index=outter(get_time)   index=inner函数的地址
@outter1  # get_time=outter1(index)   index是最原始的index的内存地址
def index():
    time.sleep(3)
    print('index')
    return 'from index'
index()
View Code

方法是在index函数上套两个装饰器语法糖

2.着重注意:

(1)装饰器在装饰函数的时候,执行顺序是是从下往上(紧贴函数到远离函数)

(2)装饰器在执行的时候,执行顺序是从上往下的(离被装饰的函数最远的装饰器最先执行,最后执行的是紧贴函数的装饰器)

八、装饰器修复技术

1.作用

(1)用户查看被 装饰了的函数 的函数名的时候,看到的就是 原函数 的内存地址

(2)用户查看被 装饰了的函数 的注释的时候,看到的就是 原函数 的注释

2.如何用

from functools import wraps
def outter(func):
    @wraps(func)  # 装饰器修复技术
    def inner(*args,**kwargs):
        '''
        这是inner函数
        :param args:
        :param kwargs:
        :return:
        '''
        print('执行被装饰函数之前 你可以执行的操作')
        res = func(*args,**kwargs)
        print('执行被装饰函数之后 你可以执行的操作')
        return res
    return inner

@outter  # index = outter(最原始的index的内存地址)
def index():
    '''
    这是index函数
    :return:
    '''
    pass
print(index)  # 查看函数的内存地址  返回的就是原函数index的
print(help(index))  # 查看函数的注释  返回的就是原函数index的
print(index.__name__)  # 查看函数名字符串形式  返回的就是index
View Code

(1)在装饰器外导入一个模块:from functools import wraps

(2)在装饰器里写入:@wraps(func),括号内的形参与装饰器内的一致

九、多层装饰器

1.习题:求index运行之后的打印结果

def outter1(func1):
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1
def outter2(func2):
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2
def outter3(func3):
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3
@outter1  # index = outter1(wapper2)
@outter2  # wrapper2 = outter2(wrapper3)
@outter3  # wrapper3 = outter3(index)  最原始的index的内存地址
def index():
    print('from index')
index()
View Code

2.代码执行顺序:

原文地址:https://www.cnblogs.com/francis1/p/11178574.html