Bootstrap

python基础 — 闭包和装饰器

一、闭包

1、闭包概念

百度百科

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

维基百科:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

注意:闭包并不只是一个python中的概念,在函数式编程语言中广泛应用。

哈哈看完有没有感觉云里雾里的的,看不懂对了,下面我们来拿Python中的闭包实例来感受下到底什么事闭包,在回过头来理解闭包的概念。

2、Python中的闭包

(1)Python中闭包的特点

  • 在一个函数(外函数)中定义了另一个函数(内函数)
  • 外函数返回值是内函数
  • 内函数引用了外函数的自由变量(外部参数,局部变量)

(2)创建一个简单的闭包

# 创建一个简单的闭包

def outer(a):  # 外函数

    def inner(b):  # 内函数
        print(a, b)  # 内函数引用了外函数的自由变量

    return inner  # 外函数返回值是内函数

(3)闭包的简单应用

问题:使用闭包计算一个数的 n 次幂。

# 创建闭包
def math_power(power):
    def math_base(base):
        return base ** power

    return math_base
# 计算 2 的平方
print(math_power(2)(2))

# 计算 2 的立方
print(math_power(3)(2))

相信到这里,我们已经可以判断是否是闭包,自定义闭包并简单的使用闭包了,下面我们来浅析闭包的一些特性原理。

(4)闭包的一些特性原理

pass

二、装饰器

1、装饰器概念

简单地说:装饰器本质上就是一个闭包,在不改变原函数的前提下拓展被装饰函数的功能。有助于让我们的代码更简短,更Python范!

使用时只需要在函数前加上@wrapper。

一个函数可以使用多个装饰器,注意装饰器的执行顺序。同理一个装饰器也可以被多个函数使用。

装饰器可以分为内置装饰器(@staticmethod,@classmethod,@property),自定装饰器两大类。

装饰器可以应用在授权,日志、事务处理、缓存等场景

2、内置装饰器

(1)@staticmethod

  • 静态方法
  • 不需要 self ,cls参数
  • 调用方式:类名.方法名()

(2)@classmethod

  • 类方法
  • cls做为方法的第一个参数,隐式的将类做为对象传递给方法,调用时无须实例化。
  • 调用方式:类名.方法名()

@property

  • 类属性
  • 使调用类中的方法像引用类中的字段属性一样。被修饰的特性方法,内部可以实现处理逻辑,但对外提供统一的调用方式。
  • 调用方式:实例名.方法名,注意不加()
     
class TestDemo():
 
    def __init__(self, *args, **kwargs):  # 使用 *args,**kwargs实现构造函数的多态性
        pass
 
    @staticmethod
    def func1():  # 注意这里不带参数 self
        print('this is staticmethod')
 
    @classmethod
    def func2(cls):  # 注意这里带参数 cls
        print('this is classmethod', cls)
 
    @property
    def func3(self):
        str = 'this is property'
        return str
 
    def func4(self):
        print('this is func4', self)
 
 
if __name__ == '__main__':
    # 实例化对象
    testdemo = TestDemo()
    print(TestDemo)
    print(testdemo)
    TestDemo.func1()
    TestDemo.func2()
    print(testdemo.func3)
    testdemo.func4()
 
--------------------------------------------------------------------------------------
运行结果:
<class '__main__.TestDemo'>
<__main__.TestDemo object at 0x0000019EC21CC4F0>
this is staticmethod
this is classmethod <class '__main__.TestDemo'>
this is property
this is func4 <__main__.TestDemo object at 0x0000019EC21CC4F0>

分析运行结果可知:

类对象:<class '__main__.TestDemo'> ,对应参数cls

实例对象:<__main__.TestDemo object at 0x0000019EC21CC4F0>,对应参数self

总结:

  • cls做为方法的第一个参数,隐式的将类做为对象传递给方法,调用时无须实例化。
  • self做为第一个参数,隐式的将类实例传递给方法,调用方法时,类必须实例化
     

3、自定义装饰器

(1)使用位置参数 *args 和关键字参数 **kwargs ,自定义一个通用的装饰器函数 wrapper。

# 通用装饰器
def wrapper(func):  # 装饰器函数
 
    def inner(*args, **kwargs):  # 接受被装饰函数传参
        print('inner')
        return func(*args, **kwargs)  # 执行被装饰函数
 
    return inner  # 闭包,外部函数返回值是内部函数
 
 
@wrapper
def test():  # 被装饰函数
    print("test")
 
test()
# print(test())    # 注意这里 fun()函数没有return,默认return None
 
------------------------------------------------------------------------
运行结果:
inner
test

装饰器函数的执行步骤:

  1. 把被装饰函数对象test 作为入参func 传给装饰器函数 wrapper
  2. 执行装饰器函数的外函数 wrapper()
  3. 执行装饰器函数的内函数 inner()
  4. 执行被装饰函数 test()

(2)多个装饰器

对于多个装饰器,和单个装饰器类似,只是把上一个装饰器返回的函数对象又作为下一个装饰器的参数传递进去了而已。

def wrapper1(func):
    def inner(*args,**kwargs):
        print('wrapper1')
        ret=func(*args,**kwargs)
        print('wrapper1')
        return ret
    return inner
 
 
def wrapper2(func):
    def inner(*args,**kwargs):
        print('wrapper2')
        ret=func(*args,**kwargs)
        print('wrapper2')
        return ret
    return inner
 
@wrapper2
@wrapper1
def test():
    print('test')
 
test()
------------------------------------------------------------------------------
运行结果:
wrapper2
wrapper1
test
wrapper1
wrapper2

4、装饰器应用场景

(1)日志装饰器

import logging
 
# 创建一个logger日志记录器对象
logger = logging.getLogger()
 
# 设置logger日志级别
logger.setLevel("DEBUG")
 
# 设置日志格式
formatter=logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
 
# 设置日志去向,输出到控制台
handler = logging.StreamHandler()
 
# 配置输出的日志格式
handler.setFormatter(formatter)
 
# 给logging日志记录器对象添加此handler
logger.addHandler(handler)
 
 
def log_info(func):
    "日志装饰器"
 
    def wrapper(*args,**kwargs):
        logger.debug(func.__name__) # 控制台打印输出日志
        return func(*args,**kwargs)
 
    return wrapper
 
@log_info
def func(a,b):
    return a + b
 
 
func(1,2)
 
-------------------------------------------------------------------
运行结果:
2021-05-06 15:15:54,231 - DEBUG - func


reference:

Python闭包概念入门 - 知乎

python 装饰器详解 - 知乎

;