【Python】高级语法——闭包和装饰器

本文详细介绍了Python的LEGB作用域规则、闭包和装饰器的概念及应用。通过实例展示了闭包如何记忆嵌套作用域的变量,以及装饰器如何用于在不修改原函数代码的情况下添加额外功能,如计时。此外,还探讨了装饰器的使用,包括带参数的装饰器和类装饰器。文章最后讨论了`functools.wraps`在装饰器中的作用,保持被装饰函数的元信息不变。
摘要由CSDN通过智能技术生成

来源:
【参考:Python必知必会 - 闭包和装饰器
【参考:Python的闭包和装饰器,我是这样理解的_哔哩哔哩_bilibili

在原有基础上添加自己的思考过程

代码可视化网站:【参考:Python Tutor: Learn Python, JavaScript, C, C++, and Java by visualizing code

总结

  1. LEGB查找原则(局部,嵌套,全局,内置作用域)
  2. Python闭包(def 嵌套一个 def,特点是能记住当时的嵌套作用域
  3. Python装饰器(一种特殊的闭包,给多个函数添加相同的功能,如计时功能)
  • def语句在这个模块文件顶层将一个函数对象赋值给变量名func
  • def是一个简单的可执行语句,可以出现在任意其他语句能够出现的地方,包括嵌套在另一个def之中。
  • 变量的作用域只与定义的位置有关,与函数调用位置无关

LEGB

Local、Enclosing function locals、Global、Built-in

当引用一个变量时,Python按以下顺序依次进行查找:

  1. 本地作用域 L
  2. 上层嵌套函数的作用域 E
  3. 全局作用域 G
  4. 内置作用域 B

第一个能够完成查找的就算成功,就会停止往下查找

s = "G"  # 3. (G) 全局作用域

def computer(value=20):
    # 嵌套作用域内的变量:value, prize函数, s
    def prize():
        print("s:", s)  # 1.(L) 本地变量
        print("inner:", value)

    s = "E"  # 2.(E) 上一层结构中的def或lambda的本地作用域
    prize()  # 变量的作用域只与定义的位置有关,与函数调用位置无关

computer(88)

"""
s: E
inner: 88
"""
word = "global"

def outer(word="TAT"):
    # 作用域:word(参数), inner函数
    print(word) # L
    def inner():
        print(word) # E
    word += "enclose"

    inner()

outer()
print(word) # G

"""
TAT
TATenclose
global

"""

闭包

【参考:python闭包的作用_whgiser的博客-CSDN博客

维基百科:在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

未使用闭包

# 为什么我们要引入闭包?
func_list = []
for k in range(1, 5):
    def func(x):
        return x * k
    func_list.append(func) # 只存入函数名,参数的值没有存入

# 上面运行完后 k=4

for func in func_list:  # 希望是 1x,2x,3x,4x
    print(func(3))      # 希望是 3, 6, 9, 12
    # 结果为 3*4=12

"""
12
12
12
12
"""

引入闭包

# 使用闭包解决上述问题
def enclose(k):
	# 嵌套作用域:参数k,函数func
    def func(x):
        return x * k
    return func

func_list = []
for k in range(1, 5):
    func_list.append(enclose(k))  # 闭包能记住当时的作用域 这里存入了函数和参数值

for func in func_list:  # 1x,2x,3x,4x
    print(func(3))      # 3, 6, 9, 12

"""
3
6
9
12
"""

备注:闭包还是记不住全局作用域内的变量的

# ============================================
# == 闭包可以记住嵌套作用域(即上一层作用域)
# ============================================
T = 1

def enclose(day):
    # 嵌套作用域 变量day,函数func
    day = day + T
    def func(x):
        print("day: ", day) # 1. 嵌套作用域内的变量day可以记住
        print(x + T) # 2. 全局变量T仍然无法记住,只有在调用func函数时才能使用T的值
    return func

f = enclose(day=7) 
# 这个函数返回一个变量名func并赋值给f,并且func记住了当时enclose嵌套作用域内的变量day

"""
全局变量T=1
day = 7+1 =8
"""

T += 1

g = enclose(day=7)
"""
全局变量T=2
day= 7+2 =9
"""


f(x=1)
"""
全局变量T=2
func():
    
    x+T = 1+2
"""
g(x=1)
"""
全局变量T=2
func():

    x+T = 1+2
"""

输出:
"""
day:  8
3
day:  9
3
"""

装饰器

装饰器就是个闭包(只不过外层def传入的参数是个函数变量),只不过多了一个语法糖

装饰器让你在一个函数的前后去执行代码。

import time

# 装饰器函数
def outer(origin): 
    # origin是一个函数名
    def inner():  
        t1 = time.time()
        origin()
        t2 = time.time()
        print(f"finish func: {origin.__name__}, time cost: {t2 - t1:.6f}(sec)")
    return inner

# --------1----------
def myfunc1():
    for i in range(100000):
        pass
    print("我是myfunc1函数")

func = outer(myfunc1) 
"""
相当于
origin = myfunc1
func = inner
"""

func()
"""
相当于调用inner()
然后会执行origin()即myfunc1()

"""

# --------2---------- 装饰器
# 语法糖 使用装饰器
@outer # myfunc2= outer(myfunc2) 在def之后执行
def myfunc2(): 
    for i in range(100000):
        pass
    print("我是myfunc2函数")
    
myfunc2()

"""
我是myfunc1函数
finish func: myfunc1, time cost: 0.001995(sec)
我是myfunc2函数
finish func: myfunc2, time cost: 0.001994(sec)
"""

典型的装饰器写法

import time


def outer(origin):
    def inner(*args, **kwargs):
        t1 = time.time()
        origin(*args, **kwargs) # 需要接收参数
        t2 = time.time()
        print(f"finish func: {origin.__name__}, time cost: {t2 - t1:.6f}(sec)")
    return inner


@outer # func1_new = outer(func1) 在def之后执行
def func1(a1):
    print("我是func1函数")
    print(a1)
    value = 11
    return value
"""
相当于
origin=func1
func1_new =inner
"""
func1(-1) # func1_new 
"""
相当于调用inner(-1) 
然后会执行origin(-1)即func1(-1)
"""

@outer
def func2(b1,b2):
    print("我是func2函数")
    print(b1,b2)
    value = 22
    return value


@outer
def func3(s="special"):
    print("我是func3函数")
    value = 33
    return value



func2(8, 9)
func3(s="time")
我是func1函数
-1
finish func: func1, time cost: 0.000000(sec)
我是func2函数
8 9
finish func: func2, time cost: 0.000000(sec)
我是func3函数
finish func: func3, time cost: 0.000000(sec)

多层装饰器

import logging

logging.basicConfig()

# 声明一个 Logger 对象
logger = logging.getLogger(__name__)

# 设置忽略级别
logger.setLevel(level=logging.INFO)

# 嵌套的装饰器
def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "info":
                logger.info("%s is running" % func.__name__)
            func(*args)
        return wrapper

    return decorator

def foo(name='foo'):
    print("i am %s" % name)

foo = use_logging(level='warn')(foo)
foo()

# ============= 等价 ==============

# python语法糖
# foo = use_logging(level='warn')(foo)
# 可以看出@后面的内容就是上行代码去掉左边的foo和右边的(foo)剩下的内容
@use_logging(level='warn')
def foo(name='foo'):
    print("i am %s" % name)

"""
level='warn'
func=foo
foo_new = decorator
"""
foo() # foo_new
"""
相当于调用decorator()
然后会执行wrapper(),wrapper里面会执行func()即foo()
"""
i am foo
i am foo

类装饰器

class myDecorator(object):
    def __init__(self, f):
        print("inside myDecorator.__init__()")
        f()  # Prove that function definition has completed
        self.g = f  # 将函数变量f设置为类属性g

    def __call__(self):
        print("inside myDecorator.__call__()")
        self.g()


def aFunction():
    print("inside aFunction()")


aFunction = myDecorator(aFunction)  # aFunction是一个类实例化后的对象

print("=== Finished decorating aFunction() ===")
aFunction() # 会调用call

print('------------------------------')
@myDecorator
def bFunction():
    print("inside bFunction()")
bFunction()
inside myDecorator.__init__()
inside aFunction()
=== Finished decorating aFunction() ===
inside myDecorator.__call__()
inside aFunction()
------------------------------
inside myDecorator.__init__()
inside bFunction()
inside myDecorator.__call__()
inside bFunction()

对象装饰器

class Decorator:
    def __init__(self, arg1, arg2):
        print('执行类Decorator的__init__()方法')
        self.arg1 = arg1
        self.arg2 = arg2

    def __call__(self, f):
        print('执行类Decorator的__call__()方法')

        # wrap = xxx
        def wrap(*args):
            print('执行wrap()')
            print('装饰器参数:', self.arg1, self.arg2)
            print('执行' + f.__name__ + '()')
            f(*args)  # 执行顾客的功能
            print(f.__name__ + '()执行完毕')

        return wrap


@Decorator('Hello', 'World')  # 2. example = Decorator('Hello', 'World')(example)
def example(a1, a2, a3):  # 1. example = xxx
    print('传入example()的参数:', a1, a2, a3)


print('装饰完毕')

print('准备调用example()')
example('Wish', 'Happy', 'EveryDay')  # 3.
print('测试代码执行完毕')
执行类Decorator的__init__()方法
执行类Decorator的__call__()方法
装饰完毕
准备调用example()
执行wrap()
装饰器参数: Hello World
执行example()
传入example()的参数: Wish Happy EveryDay
example()执行完毕
测试代码执行完毕

wraps

未使用wraps

from functools import wraps


def my_decorator(func):
    # @wraps(func)  # 防止被装饰函数example的函数名称和docstring被修改
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        func(*args, **kwargs)

    return wrapper


@my_decorator
def example(): # 被装饰函数
    """Docstring"""
    print('Called example function')


example()
print(example.__name__, example.__doc__)

"""
Calling decorated function...
Called example function
wrapper decorator
"""

使用wraps

from functools import wraps


def my_decorator(func):
    @wraps(func)  # 防止被装饰函数example的函数名称和docstring被修改
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        func(*args, **kwargs)

    return wrapper


@my_decorator
def example(): # 被装饰函数
    """Docstring"""
    print('Called example function')


example() 
print(example.__name__, example.__doc__)

"""
Calling decorated function...
Called example function
example Docstring
"""

装饰器模板

from functools import wraps


def outer(origin):
    @wraps(origin)
    def inner(*args, **kwargs):
        print("before ...") # 执行前
        sum = origin(*args, **kwargs)  # 调用原来的func函数
        print("after ...") # 执行后
        return sum
    return inner


@outer  # func = outer(func)
def func(x, y):
    print(f"x: {x}, y: {y}")
    return x + y


res = func(3,7)
print(res)
"""
before ...
x: 3, y: 7
after ...
10
"""
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值