python基础12 闭包函数与装饰器

151 阅读5分钟

闭包函数

闭包函数简介

  • 1.定义在函数内部,且内部函数使用了外部函数的名称空间中的名字的才可以被称为闭包函数

闭包函数实际应用

正确案例:    
def func():            # 1 *第一步先定义一个函数func 
    username = 'jason' # 3 *第三步运行func()函数体代码
    def index():       # 4 *第四步在内部定义一个函数index
        print(username)# 9 *第九步进入index函数体代码输出username绑定的值
    return index       # 5 *第五步返回index函数名
res = func()           # 2 *第二步先看赋值符号右边调用func()   
                       # 6 *第六步对res接收到函数名index,绑定的是index函数的所在位置
print(res)             # 7 *第七步输出res,实质输出的是index的所在位置值
res()                  # 8 *第八步此时的res()其实就是index(),调用index()
如果将上述的案例改变一下:将username = 'jason'变成func的形参呢?
def func(username):    # 给他一个形参username 
    def index():       
        print(username)
    return index       
func('jason')          # 此时函数的形参username进行了动态绑定绑定了实参jason
* 本质并未发生改变,仍然为闭包函数
错误案例:
username = 'jason' 
def index():       
    print(username)
* 此时的函数虽然被定义在了内部,但是内部函数实用的并非外部函数的名称空间中的名字。而是全局中的名字。所以不能被叫做闭包函数
  • 给函数体代码传值可以通过形参,或闭包函数完成
    • 形参传值
    def 函数名(xxx):
        print(xxx)
    
    • 闭包函数
    def 函数名():
        变量名 = '数据值'
        def 内部的函数名():
            print(变量名)
        return 内部的函数名  
    ** 比较死不灵活只能输出固定的数据值
    
     def 函数名(形参):                    # 此时的形参其实就是定义的变量名
        def 内部的函数名(): 
            print(形参)
        return 内部的函数名
     新变量名 = 函数名('要传入的数据值')    # 最后需要输出什么就传入什么
     新变量名()                           # 此时新变量名加括号其实就是调用内部的函数名
    ** 比较灵活,需要输出什么就调用
    

装饰器

装饰器的简介

  • 装饰器的本质:
    • 在不改变装饰对象的'调用方式'和'内部代码'给装饰对象新增功能
  • 装饰器的原则
    • 不能修改只能添加功能
  • 知识拓展
    • time.time() 是一个时间戳,指的是从1970年1月1日0时0分0秒,到我们现在所经历的秒数。我们可以使用时间戳统计我们程序运行的时间 image.png

装饰器前期推导

  • 统计index函数执行的时间
# 1.在调用index函数之前获取一下时间戳
# 2.在index函数调用结束后获取一下时间戳
# 3.前后获取的时间进行相减后输出

import time       # 导入python自带的时间模块
def index():
    time.sleep(3)
    print('from index')
start_time = time.time()
index()
end_time = time.time()
print(end_time - start_time)
* 但是有个缺陷:不同形参个数的函数无法兼容统计
        所以我们需要用到*args,**kwargs可变长参数
* 我们如果不改变函数调用方式,所以就有了装饰器。

装饰器的版本

装饰器入门版

import time
def index():             # 1.定义函数name
    time.sleep(1)        # 10  程序休眠一秒
    print('from index')  # 11  输出from index
    return '执行index函数之后的返回值'   #12 将文字作为返回值返回

def outer(xxx):          # 2.定义函数outer,传入一个行参xxx
    def get_time():      # 4.定义一个内部函数get_time
        start_time = time.time()  # 8.获取函数index调用前的时间戳
        xxx()                     # 9.调用index()
        end_time = time.time()    # 13.获取函数index调用后的时间戳
        print('函数的执行时间是:', end_time - start_time)   # 14.输出返回值

    return get_time      # 5.将get_time作为函数outer的返回值传出


index = outer(index)     # 3.右边调用outer函数,将实参index传给xxx
                         # 6.此时index = get_time 接收了outer的返回值
index()                  # 7.调用函数get_time  index()就是get_time()

装饰器进阶版

import time
def outer(xxx):          # 1.定义函数outer,传入一个行参xxx
    def get_time(*args, **kwargs):      # 4.定义一个内部函数get_time
        # 可变长参数打破限制任意传值,此时传值jason
        start_time = time.time()        # 8.获取函数index调用前的时间戳
        xxx(*args, **kwargs)            # 9.调用index(‘jason’)
        end_time = time.time()    # 12.获取函数index调用后的时间戳
        print('函数的执行时间是:', end_time - start_time)   # 13.输出返回值
    return get_time      # 5.将get_time作为函数outer的返回值传出

def index(name):         # 2.定义函数index,设置他的形参为name
    time.sleep(1)        # 10.
    print('from index')  # 11. 输出


index = outer(index)     # 3.右边调用outer函数,将实参index传给xxx
                         # 6.此时index = get_time 接收了outer的返回值
index('jason')           # 7.调用函数get_time  index('jason')就是get_time('jason')
  • index中的jason可以随便更换数据值。

装饰器最终版

import time
def outer(xxx): #1 
    def get_time(*args, **kwargs): #5 outer(home)执行           
        start_time = time.time() #8    
        res = xxx(*args, **kwargs) #9 调用home()          
        end_time = time.time() #12
        print('函数的执行时间是:', end_time - start_time) #13   
        return res #14
    return get_time #6     

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

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

home = outer(home)  #4 outer(home)
                    #7 home = get_time                     
xxx = home() #15 home = 具体值
print(xxx)   #16

index = outer(index)#4 
                    #7
res = index('jason')#15                      
print(res)#16

装饰器语法糖

import time

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

装饰器的模板

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
@wraps(func_name)  仅仅是为了让装饰器不容易被别人发现 做到真正的以假乱真

练习

from functools import wraps
def inner(func):
    @wraps(func)
    def action(*args, **kwargs):
        global user_id
        if user_id:
            res = func(*args, **kwargs)
            return res
        else:
            username = input("用户名:").strip()
            password = input("密码:").strip()
            if username == "jason" and password == "123":
                user_id = True
                res = func(*args, **kwargs)
                return res
            else:
                print("权限不足")
                return
    return action
@inner
def login():
    print("开始登录")
@inner
def register():
    print("开始注册")
@inner
def transfer():
    print("开始转变")
@inner
def withdraw():
    print("开始撤回")
user_id = False
login()

结果:

image.png image.png