Day20 函数闭包与装饰器

一. 函数闭包

【locals()打印当前环境的局部变量】

闭包:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,则把这个使用外部函数变量的内部函数称之为闭包

# 1.定义内部函数:
def outer(num):
    # 2.定义内部函数,在内部函数中使用外部函数的变量
    def inner(num1):
        print(num + num1)
    
    # 3.外部函数返回内部函数的地址
    return inner # 函数名字就是函数地址

if __name__ == '__main__':
# 创建闭包实例
func = outer(100) # 此时func相当于是inner

# 调用闭包
func(10) # 此时调用inner函数,保存的num值为100

# 可以创建多个闭包实例,不同的闭包实例之间,不会相互影响


# 输出
110

函数内部想要修改全局变量,使用global关键字。在闭包函数内部,想要修改外部函数的局部变量,则使用nonlocal关键字

def outer():
    num = 10

    def inner():
        # num = 100 # 不是修改外部变量的值,重新定义的局部变量
        nonlocal num # 声明使用外部变量 num 不重新定义
        num = 100

    print(f'调用inner之前:{num}')
    inner()
    print(f'调用inner之后:{num}')

    return inner

func = outer()
func()


# 输出
调用inner之前:10
调用inner之后:100

二. 装饰器

本质是函数,功能是为其他函数添加附加功能

原则:

  1. 不修改被修饰函数的源代码
  2. 不修改被修饰函数调用方法

装饰器 = 高阶函数 + 函数嵌套 + 闭包

高阶函数

  1. 函数接收的参数是一个函数名
  2. 函数的返回值是一个函数名
  3. 满足上述条件任意一个,都可称为高阶函数

使用高阶函数实现装饰器

import time

def foo():
    print('test one!')


def timer(func):
    start_time = time.time()
    func()
    stop_time = time.time()

    print('Running Time is %s' %(stop_time - start_time))

    return func

f00 = timer(foo)
f00()


# 输出
test one!
Running Time is 0.0
test one!
# 返回了func,多运行了一次

import time

def foo():
    print('test one!')

def timer(func):
    start_time = time.time()
    return func
    stop_time = time.time()
    print('Running Time is %s' %(stop_time - start_time))

foo = timer(foo)
foo()

# 输出
test one!
# 又没有实现计时功能,在中间就已经结束了

高阶函数虽然没有修改被修饰函数的源代码,也没有修改被修饰函数的调用方式,但是也没有为被修饰函数添加新功能,所以高阶函数不足够实现装饰器

装饰器框架

def timmer(func):
    def wrapper():
        print(func)
        func()
    return wrapper

 通过装饰器实现程序运行时间的计时功能

import time

def timmer(func):
    def wrapper():
        start_time = time.time()
        func()           
        stop_time = time.time()
        print('Running Time is %s' %(stop_time - start_time))
    return wrapper

# 测试函数
@timmer 	# @等同于 test = timmer(test)
def test():
    all = 0
    for i in range(100):
        time.sleep(0.0001)
        all += i
    return all    # 返回值是空??

# test = timmer(test)   
print(test())  # 实际运行的是wrapper()

# 输出
Running Time is 1.5582449436187744
None

这样虽然得到了运行时间,但却多返回了一个None值,因为在装饰器内部函数虽然执行了test()函数,但返回的是wrapper()函数,而wrapper()函数并没有返回任何值,所以输出了None

可以接收到返回值版本

#装饰器
import time

def timmer(func):
    def wrapper(a, b, c):
        start_time = time.time()
        res = func(a, b, c)
        stop_time = time.time()

        print('Running Time is %s' %(stop_time - start_time))

        # 在这里把func()返回值赋值后再返回
        return res
    return wrapper

#测试函数
@timmer #equal test = timmer(test)
def person(name, age, gender):
    return '返回 %s %s %s' %(name, age, gender)

#test = timmer(test)
print(person('luke', 18, 'male'))

# 输出
Running Time is 0.0
返回 luke 18 male

如果面对不同函数,由于不同函数参数个数不同,这样的写法显然不能满足所有函数

import time

def timmer(func):
    def wrapper(*args, **kwargs): # *args相当于解压序列,所以当传入超出参数个数的时候会报错
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()

        print('Running Time is %s' %(stop_time - start_time))
        return res

    return wrapper

#测试函数
@timmer # test = timmer(test)
def father(name, age, gender):
    return '返回信息 %s %s %s' %(name, age, gender)

def son(name, age, gender, school):
    return '返回信息 %s %s %s %s' %(name, age, gender, school)

# test = timmer(test)
print(father('luke', 38, 'male'))
print(son('alex', 13, 'male', 'beijing'))

1、*args和**kwargs 这两个是python中方法的可变参数。

2、*args表示任何多个无名参数,它是一个tuple;

3、**kwargs表示关键字参数,它是一个dict。并且同时使用*args和**kwargs时,必须*args参数列要在**kwargs前,像foo(a=1, b='2', c=3, a', 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”。

4、当方法的参数不确定时,可以使用*args 和**kwargs

叠加多个装饰器时

  1. 加载顺序(outter函数的调用顺序):自下而上
  2. 执行顺序(wrapper函数的执行顺序):自上而下
def outter1(func1): #func1=wrapper2的内存地址
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def outter2(func2): #func2=wrapper3的内存地址
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def outter3(func3): # func3=最原始的那个index的内存地址
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3

@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
    print('from index')

print('======================================================')
index()

# 输出
加载了outter3
加载了outter2
加载了outter1
======================================================
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index

解压序列

G = [1,3,4,5,6,6,6,3,2,3,23,2,12,31,231,23,12,31,23,1,244]

a, *_, c = G    # a=1, c=244

python可以快速交换变量值:f1, f2 = f2, f1

而c语言则需要:temp=b; b=a; a=temp;

三. 用装饰器做一个验证功能

# 带参数,带返回值
def verify(func):
    def wrapper(*args, **kwargs):
        name = input('Username:')
        pwd = input('Password:')
        if name == 'luax' and pwd == '123':
            res = func(*args, **kwargs)
            flag = True
            return res
        else:
            print('Wrong Username or Password!')
    return wrapper

@verify
def index(name):
    print('Welcome!%s' %name)

@verify
def home(name):
    print('Welcome!%s' % name)

@verify
def cart():
    print('【%s, %s, %s】' %('吃', '东', '西'))

index('luax')
home('luax')
cart()
# 每一次都要重新输入账号密码
# 无参数版本

# 用户列表
user_list = [{'name':'a', 'password':'a'},
             {'name':'b', 'password':'b'},
             {'name':'c', 'password':'c'},
             ]

# 保存当前登录用户状态
current_user = {'name':None, 'login_status':False}

def verify(func):
    def wrapper(*args, **kwargs):
        if current_user['name'] or current_user['login_status']:
            res = func(*args, **kwargs)
            return res
        name = input('Username:')
        pwd = input('Password:')
        for user_dict in user_list:
            if name == user_dict['name'] and pwd == user_dict['password']:
                res = func(*args, **kwargs)
                current_user['name'] = name
                current_user['login_status'] = True
                return res
        else:	#和for一个代码块,可以让if一直循环
            print('Wrong Username or Password!')
    return wrapper
    
@verify
def index(name):
    print('Welcome!%s' %name)
    
@verify
def home(name):
    print('Welcome!%s' % name)
    
@verify
def cart():
    print('【%s, %s, %s】' %('吃', '东', '西'))

index('luax')
home('luax')
cart()

可以用全局变量和函数闭包来模拟session

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值