【2022-07-05】Python闭包函数与装饰器

python闭包函数与装饰器

闭包函数简介

1.定义在函数内部的函数

2.内部函数使用了外部名称空间中的名字

PS:只有符合上述两个特征的函数才能称之为是闭包函数

def function(username):    # 形参username,当被调用时会与实参发生临时绑定关系,以什么等于什么的方式,会在function局部名称空间存放一个名字
    # username = '张三'

    def index():
        print(username)

    return index


# 上述就是一个闭包函数,首先index是定义在function函数内部的,其次username是属于function局部名称空间的名字

# 那么如何在全局名称空间里调用index呢,即从全局名称空间里调用局部名称空间名字index

# 明确当前在哪个名称空间里,然后按照名字的查找顺序:局部名称空间——全局名称空间——内置名称空间
# res = function()
# print(res)                               # <function function.<locals>.index at 0x000001800F267820>
# res()                                    # 张三

res = function('张三')                     # username = '张三'
print(res)                                 # <function function.<locals>.index at 0x0000024A26BB7820>
res()                                      # 张三


闭包函数,外层函数的名称空间不会随着外层函数体代码运行结束后关闭,直到整个py文件都不在进行调用内部名称空间里的其他闭包函数时
它会自我进行检测,看看是否会被调用,如果有则不关闭,如果没有则自动关闭

闭包函数的实际应用

给函数体代码传值的方式一:通过形参
def function(xxx):
    print(xxx)


function(666)


给函数体代码传值的方式二:闭包函数
def index(username):                  # 1.定义一个函数index
    # username = 'Lisa'               # 3.并且在index局部名称空间里存放了一个username,让它指向了数据值lisa

    def function():                   # 4.在index的名称空间里也定义了一个function函数
        print(username)  

    return function                   # 5.把function返回到全局名称空间里

# 6.res就是function函数名
res = index('lisa')                  # 2.执行index函数调用,把lisa传给函数index的形参username,产生一个index的局部名称空间
                                     
res()  # 7. 开始调用function函数,然后在function局部空间里找username,如果没有就在外层即index的局部名称空间里找username,此时得到的结果就是Lisa                         
res1 = index('tony')  # 8.把之前的名称空间清除,重新调用index函数,重复上面的步骤
res1()                               # tony

装饰器简介及时间戳

装饰器的作用:在不改变被装饰对象原有的调用方式和内部代码的情况下给被装饰对象添加额外的功能

装饰器的原则:对修改封闭,对扩展开放

时间戳的使用:

import time

print(time.time())                   # 1657028359.8169787

# 时间戳(秒数):当前距离1970年1月1日0时0分0秒所经历的秒数

# 实际应用:统计代码的运行时间
start_time = time.time()
for i in range(100000):
    print(i)
end_time = time.time()
print('for循环的执行时间是:%s' % (end_time - start_time))        # for循环的执行时间是:0.8020210266113281

time.sleep(5)
"""让程序原地等待五秒钟"""
print('晚上好呀')                # 指针停留五秒钟后打印   晚上好呀


装饰器推导流程

import time


def index():
    time.sleep(4)
    print('please tell me what is the time for index')


# 在不改变index调用方式和内部代码的情况下,统计index的执行时间

# start_time = time.time()    		    # 在调用index函数之前获取时间戳
# index()  						      # 调用index函数
# end_time = time.time()
# print(end_time - start_time)          # please tell me what is the time for index       4.008287191390991

# 1.缺陷1  如果有多个相同的函数需要被统计时间,则需要重复编写执行代码
# 解决措施:封装成函数
def get_time():
    start_time = time.time()
    index()   # 调用index函数
    end_time = time.time()
    print('函数的执行时间是:', end_time - start_time)

# get_time()                               # please tell me what is the time for index  函数的执行时间是: 4.007498025894165



# get_time()                               # please tell me what is the time for index  函数的执行时间是: 4.007647275924683



# get_time()                               # please tell me what is the time for index  函数的执行时间是: 4.013478994369507


# 2.缺陷2  如果有多个不同的函数需要统计时间,那么封装函数的措施就不够完善了
# 解决措施:可以通过给函数体传形参的方式来打破限制


def home():                # 1.定义一个函数home
    time.sleep(5)
    print('please tell me what is the time for home')

def get_time(xxx):         # 2.定义一个函数get_time              
    start_time = time.time()                     # 在调用index函数之前获取时间戳
    xxx(*args, **kwargs)   # 调用index函数
    end_time = time.time()
    print('函数的执行时间是:', end_time - start_time)

# get_time(index)              # please tell me what is the time for index    函数的执行时间是: 4.008310079574585
# get_time(home)               # please tell me what is the time for home     函数的执行时间是: 5.007116317749023


# 3.缺陷3   不同形参个数的函数,无法兼容统计
# 解决措施   利用*args **kwargs  目前代码中无法实现

def function(a):
    time.sleep(2)
    print('please tell me what is the time for function')
get_time(function)            # 无法直接调用,需要进行传值


# 4.缺陷4  改变了原来的调用方式
# 解决措施  装饰器推导流程

# 4.缺陷4  改变了原来的调用方式
# 解决措施  装饰器推导流程



装饰器初始版本

# 函数体代码需要不确定的数据  有几种给的方式

# 方式一:通过形参的方式进行传参,但是无法实现不改变调用方式
# 方式二:闭包函数
def outer(xxx):
    # xxx = index
    def get_time():         # 2.定义一个函数get_time
        start_time = time.time()                     # 在调用index函数之前获取时间戳
        xxx()   # 调用index函数
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
    return get_time


# res = outer(index)
# res()                      # please tell me what is the time for index   函数的执行时间是: 4.0019402503967285
# res = outer(home)
# res()                      # please tell me what is the time for home    函数的执行时间是: 5.004338026046753

index = outer(index)
index()                      # please tell me what is the time for index   函数的执行时间是: 4.017440319061279
home = outer(home)
home()                       # please tell me what is the time for home    函数的执行时间是: 5.004836082458496

装饰器进阶版本

import time                                          # 针对有参函数与无参函数如何兼容

def outer(xxx):
    # xxx = index
    def get_time(*args, **kwargs):                   # *与**在形参中的使用方式
        start_time = time.time()                     # 在调用index函数之前获取时间戳
        xxx(*args, **kwargs)   # 调用index函数
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
    return get_time


def home():
    time.sleep(3)
    print('what is the time for home')


def index(name):
    time.sleep(2)
    print('what is the time for index')

def function(x,y,z):
    time.sleep(3)
    print('what is the time for function')

home = outer(home)
home()                             # what is the time for home           函数的执行时间是: 3.003410577774048

index = outer(index)        
index('tony')                      # what is the time for index          函数的执行时间是: 2.0139412879943848

function = outer(function)         
function(6,7,8)                    # what is the time for function       函数的执行时间是: 3.015282392501831

装饰器高级版本

import time

def outer(xxx):
    def get_time(*args, **kwargs):
        start_time = time.time()  # 在调用index函数之前获取一下时间戳
        res = xxx(*args, **kwargs)  # 调用index函数
        end_time = time.time()
        print('函数的执行时间是:', end_time - start_time)
        return res
    return get_time

def home():
    time.sleep(2)
    print('from home')
    return '执行home函数之后的返回值'

def index(name):
    time.sleep(1)
    print('from index')
    return '执行index函数之后的返回值'

home = outer(home)
xxx = home()
print(xxx)


index = outer(index)
res = index('jason')
print(res)

# home = outer(home)
# home()

# def func(a,b,c):
#     time.sleep(1)
#     print('from func')
#
# index = outer(index)
# index('jason')
#
# home = outer(home)
# home()
#
# func = outer(func)
# func(1,2,3)

装饰器最终版本(模板)

from functools import wraps
def outer(func_name):
    @wraps(func_name)  # 仅仅是为了让装饰器不容易被别人发现 做到真正的以假乱真
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res
    return inner

# import time
# def home():
#     time.sleep(1)
#     print('from home')
#     return 'home返回值'


# home = outer(home)
# res = home()
# print(res)
"""
执行home函数之前需要添加校验用户身份的功能
"""

# 装饰器语法糖
import time

@outer  #  home = outer(真正的函数名home)
def home():
    '''我是home函数 我要热死了!!!'''
    time.sleep(1)
    print('from home')
    return 'home返回值'

# help(home)
# print(home)
home()

# def index():
#     '我是index函数 我的功能很强大'
#     pass
#
# help(index)

课题演练

1.编写一个用户认证装饰器
  基本要求
   执行每个函数的时候必须先校验身份 eg: jason 123
  拔高练习(有点难度)
   执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
     函数:register login transfer withdraw 
   提示:全局变量 记录当前用户是否认证
   

from functools import wraps

def outer(user_auth):
    @wraps(user_auth)
    def inner(*args, **kwargs):
            username = input('请输入您的用户名>>>>:')
            password = input('请输入您的密码>>>:')
            if username == 'jason' and password == '123':
                print('登录成功')
                res = user_auth(*args, **kwargs)
                return res
            else:
                print('用户名密码错误,请重新输入')
    return inner()

@outer
def user_auth():
    print('这是一个函数体代码')
   
 
posted @   dy12138  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示