python之闭包

前言

闭包作为python高级特性中的一个,初学总觉其披着一层神秘的面纱,这里我们来一起揭开这层面纱吧。那什么是闭包呢?

闭包,又称闭包函数,和普通的嵌套函数类似,闭包中包含外部函数和内部函数,不同之处是闭包中外部函数返回的不是一个值,而是内部函数,即闭包对象;通常,该返回的函数会被赋值给一个变量,继而被调用执行。

为何要是用闭包

比如,已知三条直线上某一个点的横坐标x,求其纵坐标y。如果不使用闭包,实现如下:

def line1(x):
    return 2 * x + 1
​
def line2(x):
    return 3 * x + 2
​
def line3(x):
    return 5 * x + 3
​
if __name__ == '__main__':
    print(line1(2))
    print(line2(2))
    print(line3(2))

使用闭包,实现如下

# 闭包
def line_closure(a, b):
    def line(x):
        return a * x + b
    return line
​
if __name__ == '__main__':
    line4 = line_closure(2, 1) # 返回闭包对象,赋值给line4
    print(line4(2))            # 此处可调用闭包里的内部函数
    line5 = line_closure(3, 2)
    print(line5(2))
    line6 = line_closure(5, 3)
    print(line6(2))

5 8 13 5 8 13

以上两种方法结果一样,在只有三条直线的情况下,代码量区别不大,而当有几百条直线呢,我们是不是还要定义几百个函数呢,此时用闭包就会使代码变得简单美观,提高了代码的复用率。

闭包应用举例

给不同项目记录日志

import logging
​
def log_header_closure(project_name):
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(name)s] %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    logger = logging.getLogger(project_name)
​
    def _logging(message, level):
        if level == 'debug':
            logger.debug(message)
        elif level == 'warning':
            logger.warning(message)
        elif level == 'error':
            logger.error(message)
    return _logging
​
if __name__ == '__main__':
    project_1_logger = log_header_closure('project_1')
    project_1_logger('this is a warning info', 'warning')
    project_1_logger('this is a error info', 'error')
​
    project_2_logger = log_header_closure('project_2')
    project_2_logger('this is a warning info', 'warning')
    project_2_logger('this is a error info', 'error')
​

2021-10-27 14:21:32 [project_1] WARNING this is a warning info

2021-10-27 14:21:32 [project_1] ERROR this is a error info

2021-10-27 14:21:32 [project_2] WARNING this is a warning info

2021-10-27 14:21:32 [project_2] ERROR this is a error info

闭包和装饰器

其实,装饰器就是闭包,只不过装饰器的外部函数参数是一个函数,内部函数对该函数进行加工修饰最后返回。

def add(x, y):
    return x + y
​
# 装饰器,实际上就是一个闭包
def wrap_outer(func):
    def wrap_inner(x, y):
        print(time.asctime())
        print('run start')
        result = func(x, y)
        print('run finished')
        print(time.asctime())
        return result
    return wrap_inner
​
if __name__ == '__main__':
    wrapper = wrap_outer(add)
    print(wrapper(2, 3))

Wed Oct 27 11:59:19 2021

run start

run finished

Wed Oct 27 11:59:19 2021

5

接着,我们用python装饰器的“语法糖”来重新实现一下,让代码看起来更优雅些:

from functools import wraps
​
def wrap_outer(func):
    @wraps(func)
    def wrap_inner(*args, **kwargs):
        print(time.asctime())
        print('run start')
        result = func(*args, **kwargs)
        print('run finished')
        print(time.asctime())
        return result
    return wrap_inner
​
@wrap_outer
def add(x, y):
    return x + y
​
if __name__ == '__main__':
    print(add(2, 3))

Wed Oct 27 13:39:55 2021

run start

run finished

Wed Oct 27 13:39:55 2021

5

可以看出,执行结果跟上面闭包实现一模一样。

闭包和普通函数

闭包比普通函数多了一个'__closure__'属性,用来记录引用的外部函数的自由变量的地址。当闭包被调用时,会从该地址获取到该自由变量,并完成整体的函数调用。

如上面的日志例子:

project_1_logger = log_header_closure('project_1')
print('111', project_1_logger.__closure__)

(<cell at 0x03AF7370: Logger object at 0x0E56D1B0>,)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值