闭包函数,装饰器详解

闭包函数

【1】闭包函数两大特征

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

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

【2】在代码中怎么走

def outer():
    def inner():
        print(f'真简单')
    return inner

res = outer()

在上述代码中怎么走

  • 第一步定义outer函数,什么时候执行这个函数里的代码,要遇到outer()才会执行,
  • 第二步到res = outer(),=是赋值符号所以先看outer()
  • 第三步有了outer()才会执行里面的代码,进入到里面执行def inner():
  • 第四步因为没有inner()所以inner函数里面的代码不会执行,只会返回一个inner函数名
  • 第五步,然后把inner这个函数名给了res这个变量,使得res指向了inner函数里的函数体
    • 然后res()就相当于inner()
def outer():
    def inner():
        print(f'真简单')
    return inner

res = outer()
res() # 真简单

现在inner()是不是闭包函数

def outer():
    def inner():
        print(f'真简单')
    return inner
# 现在只是只是在outer函数内部,并没有使用了外层函数名称空间中的名字

def outer():
    x = 741
    def inner():
        print(f'真简单',x)
    return inner
# 现在inner算是真正的闭包函数

执行代码顺序

# 最后输出999还是741
def outer():
    x = 741
    def inner():
        print(f'真简单',x)
    return inner
x=999
res = outer()
res()  # 真简单 741
# inner是在outer 局部里面的的局部里面的,inner本身没有x,所以他会往上找,就找到outer局部里面的x=741,跟x=999无关

【3】闭包函数实际应用

闭包函数是给函数整体传参的另一种方式

函数体传参方式一:形参


def index(username):
    print(username)   
 
# 函数体代码需要什么就可以在形参中写什么
index('silence')  # silence # 在调用index这个函数时就必须给他一个username参数

函数体传参方式二:闭包

def outer():
    username = 'silence'
    def index():
        print(username) # 永远使用的都是silence
    return index

res = outer()
res()    # silence

多个传递

def outer(username):
    # username = 'silence'
    def index():
        print(username)
    return index

res = outer('silence')
#形参username与值silence临时绑定>>>放在了outer局部名称空间中
res()  # silence
rest = outer('happy')
rest() # happy

代码执行顺序图

装饰器

【1】简介

装饰器是由名称空间,函数名,闭包函数等整合到一起的产物

【2】装饰器定义

不改变被装饰对象原有的调用方式内部代码的情况下

给被装饰对象添加新功能

【3】装饰器原则

对扩展开发,对修改封闭

【4】装饰器了解

import  time
print(time.time()) #1713259590.5478706
# 上述数字是时间戳:1970年1月1日0时0分0秒距离刚刚代码运行的秒数


import  time
time.sleep(4)  # 让程序原地等待四秒
print("你真棒")


import  time

def index():
    time.sleep(3)
    print('from index')
star_time = time.time() # 函数执行之前获得一个时间戳
index()
end_time = time.time() # 函数执行之后获得一个时间戳
print(end_time - star_time)  # 两个时间戳的差值就是函数的执行时间
# from index 3.000314950942993 

【5】代码怎么生成一个装饰器

封装成函数之后,调用方式改变了,不符合装饰器原则

#缺陷一:
    # 代码写死了,无法统计其他函数的执行时间 只有index()
    # 如何解决:----> 将函数名通过形参的形式传入,例如get_time(in)
# 缺陷二:    
    # 封装成函数后 以下代码中 index()调用方式改变成了get_time(index)
    # 通过形参的形式传入解决不掉
import  time

def index():
    time.sleep(3)
    print('from index')

def get_time():
    star_time = time.time() # 函数执行之前获得一个时间戳
    index()
    end_time = time.time() # 函数执行之后获得一个时间戳
    print(end_time - star_time)  # 两个时间戳的差值就是函数的执行时间

get_time()

装饰器简易版本(无参装饰器)

import  time
def index():
    time.sleep(3)
    print('from index')
def outer(func):
    def get_time():
        star_time = time.time() # 函数执行之前获得一个时间戳
        func()  # 调用了真正的index函数
        end_time = time.time() # 函数执行之后获得一个时间戳
        print(end_time - star_time)  # 两个时间戳的差值就是函数的执行时间
    return get_time  

index = outer(index)
index() # 看似调用的是index其实调用的是get_time
print(index)  # 全局名称空间中的index指向的是get_time函数体代码

执行顺序

装饰器进阶版本(有参装饰器)

解决的是参数问题

程序报错

import  time
def home(a):
    time.sleep(3)
    print('from home')
def outer(func_name):
    def get_time():
        star_time = time.time()
        func_name()
        end_time = time.time()
        print(end_time - star_time)
    return get_time

home = outer(home)
home() # 报错 home() missing 1 required positional argument: 'a'

函数体代码里需要一个参数,有两种办法

  • 方式一:直接形参传
  • 方式二:再进行一次闭包操作
import  time
def home(a):
    time.sleep(3)
    print('from home')
def outer(func_name):
    def get_time(a):     # 意思就是在调用get_time时候也要给他传递一个参数
        star_time = time.time()
        func_name(a)
        end_time = time.time()
        print(end_time - star_time)
    return get_time

home = outer(home)
home('silence')     # home就是调用的get_time 所以需要一个参数'silence'
# from home
# 3.011603593826294

我们根本不知道即将执行的函数是否需要参数但是还想让他正常运行,于是


import  time
def index():
    time.sleep(2)
    print("from index")
def home(a):
    time.sleep(3)
    print('from home')
def outer(func_name):
    def get_time(*arga,**kwargs):
        star_time = time.time()
        func_name(*arga,**kwargs)
        end_time = time.time()
        print(end_time - star_time)
    return get_time

home = outer(home)
home('silence')
index = outer(index)
index()
# from home
# 3.008333921432495
# from index
# 2.011590003967285

图解

完整版解释器

解决的是返回值问题

import  time
def index(a,b):
    time.sleep(2)
    print(a, b, "from index")
    return '加油!'

def outer(func_name):
    def get_time(*arga,**kwargs):
        star_time = time.time()
        func_name(*arga,**kwargs)
        end_time = time.time()
        print(end_time - star_time)
    return get_time


index = outer(index) # 接收函数的返回值
res = index(11,22)
print(res)
# 11 22 from index
# 2.0102128982543945
# None

为什么是返回None,其实是拿到的是get_time的返回值,但是get_time没有返回值,所以是None

想让index有返回值怎么办

import  time
def index(a,b):
    time.sleep(2)
    print(a, b, "from index")
    return '加油!'

def outer(func_name):
    def get_time(*arga,**kwargs):
        star_time = time.time()
        res=func_name(*arga,**kwargs)  # 执行真正的index函数
        end_time = time.time()
        print(end_time - star_time)
        return res
    return get_time


index = outer(index) # 接收函数的返回值
res = index(11,22)
print(res)

装饰器模板

# 万能模板
def outer(func_name): # func_name 用于接收被装饰的对象(函数)
    def inner(*args, **kwargs):
        print('执行被装饰函数之前,可以做的额外操作')
        res = func_name(*args, **kwargs) # 执行真正的被装饰函数
        print('执行被装饰函数后,可以做的额外操作')
        return res # 返回真正函数的返回值
    return inner

例题:验证登陆

def home(*args, **kwargs):
    print('我是home函数,只有silence才能调用')

def outer(func_name):
    def inner(*args, **kwargs):
        # 执行home前先获取用户数据
        username = input('username>>>:').strip()
        password = input('password>>>:').strip()
        if username == 'silence' and password == '741':
            res = func_name(*args, **kwargs) # 执行真正的被装饰函数
            return res  # 返回真正函数的返回值
        else:
            print('你没有资格执行')
    return inner
home = outer(home)
home()
# username>>>:silence
# password>>>:741
# 我是home函数,只有silence才能调用

或者是

def index():
    print("Hello")


def outer(func_name):
    def inner(*args, **kwargs):
        # 执行home前先获取用户数据
        username = input('username>>>:').strip()
        password = input('password>>>:').strip()
        if username == 'silence' and password == '741':
            res = func_name(*args, **kwargs) # 执行真正的被装饰函数
            return res  # 返回真正函数的返回值
        else:
            print('你没有资格执行')
    return inner
# home = outer(home)
# home()
index = outer(index)
index()
# username>>>:silence
# password>>>:741
# Hello

装饰器语法糖

仅仅是让代码编写的更加好看,简洁

def outer(func_name):
    def inner(*args, **kwargs):
        print('执行函数之前的操作')
        res = func_name(*args, **kwargs)
        return res
    return inner


@outer   # 写在想要装饰的函数的头上   等价于index = outer(index)
def index(*args, **kwargs):
    print('from index')
@outer   # 等价于home = outer(home)
def home():
    print('from home')
# index = outer(index)  # 总感觉这一行代码有些low
index()
home()
# 执行函数之前的操作
# from index
# 执行函数之前的操作
# from home

语法糖内部原理

1.使用时最好跟在被装饰对象的上方

2.语法糖会自动将下面紧挨着的函数名传给@后面的函数调用

装饰器修复技术

1. help可以查看指定函数的注释信息

ef outer(func_name):
    def inner(*args, **kwargs):
        print('执行被装饰器之前可以做的操作')
        res = func_name(*args, **kwargs)
        return res
    return inner


@outer   # 写在想要装饰的函数的头上   等价于index = outer(index)
def index(*args, **kwargs):
    print('from index')
   # 等价于home = outer(home)
def home():
    '''这是一个注释'''
    print('from home')
print(help(home)) 
help(len)
# home()
    # 这是一个注释
    
# len(obj, /)
    # Return the number of items in a container.

2. 装饰器修复

语法固定搭配

  • from functools import wraps
    • @wraps(func_name)
from functools import wraps
def outer(func_name):  # func_name 用于接收被装饰的对象(函数)
    @wraps(func_name)
    def inner(*args, **kwargs):
        print('执行被装饰器之前可以做的操作')
        res = func_name(*args, **kwargs)
        return res
    return inner


@outer   # 写在想要装饰的函数的头上   等价于index = outer(index)
def index(*args, **kwargs):
    print('from index')
   # 等价于home = outer(home)
@outer
def home():
    print('from home')

index()
home()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值