前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >闭包和装饰器

闭包和装饰器

作者头像
@小森
发布2024-03-15 10:23:36
510
发布2024-03-15 10:23:36
举报
文章被收录于专栏:xiaosenxiaosen

闭包

在函数内部再定义?个函数,并且这个内部函数?到了外部的变量,这个函数以及?到外部函数的变量及参数叫闭包

代码语言:javascript
复制
def fun_a(num_a):
??# 在函数内部再定义?个函数,并且这个内部函数?到了外部的变量
??def fun_b(num_b):
????print("in test_in 函数, number_in is %d" % num_b)
????return num_a + num_b
??# 这?返回的就是闭包的结果
??return fun_b
# 给fun_a函数赋值,这个10就是给参数num
ret = fun_a(10)
# 注意这?的10其实给参数fun_b
print(ret(10))

再比如二次函数:

代码语言:javascript
复制
def line_conf(a, b):
??def line(x):
????return a * x + b
??return line

line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))         # 结果  6
print(line2(5))         # 结果  25

从这段代码中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。

我们只需要变换参数a,b,就可以获得不同的直线表达函数。

因此,闭包也具有提?代码可复?性的作?。如果没有闭包,我们需要每次创建函 数的时候同时说明a,b,x。

代码语言:javascript
复制
def fun_a():
??fun_list = []
??for i in range(1, 4):
????def fun_b():
??????return i * i
????fun_list.append(fun_b)
??return fun_list

f1, f2, f3 = fun_a()
print(f1(), f2(), f3())

这段代码中,我们想得到是1,4,9,可是执行结果却是9,9,9

这是因为,返回的函数引用了变量 i ,但不是立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3

最简单的方法就是改为def fun_b(_i=i),这样就每次都把i值传入了

装饰器

装饰器的作用就是为已经存在的对象添加额外的功能

代码语言:javascript
复制
def test1(func):
    def test2():
        print('帮你把饭做好')
        func()
        print('洗碗')
    return test2


@test1    # 相当于eat=test1(eat)
def eat():
    print('我正在吃饭')

执行结果为: 帮你把饭做好 我正在吃饭 洗碗

这样就可以不用做饭洗碗,直接吃饭了

再比如,下面我们用装饰器logger来给所有关于work的函数增加记录日志的功能

代码语言:javascript
复制
from functools import wraps
import time

def logger(func):  # 定义一个记录日志的装饰器
    @wraps(func)
    def write_logging():
        print('[info]--时间是:%s'%time.strftime('%H:%M:%S',time.localtime()))  # 格式化
        func()
    return write_logging

@logger   # 使用装饰器给所有的work增加记录日志的功能
def work():     # 这里使用装饰器的函数不能带参数
    print('我在工作')

结果为:[info]--时间是:16:33:02 ? ? ? ? ? ? 我在工作

@wraps(func)作用是用func函数,即原函数来封装高阶函数,因为work函数调用装饰器后函数名其实是write_logging,加上@wraps(func)后函数名还是func,相当于在原函数基础上增加功能。

上面的装饰器,在work函数中不能传参数,不能知道是谁在哪个时间工作,我们如果想给func函数增加参数,就要在write_logging处增加参数:

代码语言:javascript
复制
def logger(func):  # 定义一个记录日志的装饰器
    @wraps(func)
    def write_logging(*args,**kwargs):   # 可以传多个参数,*args,**kwargs多种参数类型
        print('[info]--时间是:%s'%time.strftime('%H:%M:%S',time.localtime()))  # 格式化
        func(*args,**kwargs)
    return write_logging

@logger
def work_2(name):    
    print('%s 在工作'%name)

work_2('张三')

我们也可以给装饰器本身设置参数,比如我们要把这些数据输入到哪个文件:

代码语言:javascript
复制
def main_logger(log_file='out.log'):     # 不写的话就是默认值
    def logger(func):  # 定义一个记录日志的装饰器
        @wraps(func)
        def write_logging(*args,**kwargs):
            print('[info]--时间是:%s'%time.strftime('%H:%M:%S',time.localtime()))
            with open(log_file,'a') as f:
                f.write('log'+'\n')
            func(*args,**kwargs)
        return write_logging
    return logger

@main_logger('work2.log')   # 再嵌套一层函数,这样就可以传入想写的参数
def work_2(name):
    print('%s 在工作'%name)

work_2('张三')

我们在写代码时如果才想起来调用库函数,可以先写需要的函数,alt加enter快速调用库~

在我们的函数,线程进程等知识点都可以包装成一个类,再由我们自己去手动调用,这样不仅方便,还可以提高代码的可重用性。我们也可以将装饰器封装成一个类:

代码语言:javascript
复制
class Logs(object):
    def __init__(self,log_file='out.log',level='info'):
        self.log_file=log_file
        self.level=level

    def __call__(self,func):    # 定义装饰器的,需要有一个接受函数
        @wraps(func)
        def write_logging(*args, **kwargs):
            print('[%s]--时间是:%s' % (self.level,time.strftime('%H:%M:%S', time.localtime())))
            with open(self.log_file, 'a') as f:
                f.write('log' + '\n')
            func(*args, **kwargs)

        return write_logging

@Logs(log_file='work2.log',level='W')   # 在__init__中设置的传入参数
def work_2(name,name2):
    print('%s和%s在工作'%(name,name2))

work_2('张三','李四')
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 闭包
  • 装饰器
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com