来源:
【参考:Python必知必会 - 闭包和装饰器】
【参考:Python的闭包和装饰器,我是这样理解的_哔哩哔哩_bilibili】
在原有基础上添加自己的思考过程
代码可视化网站:【参考:Python Tutor: Learn Python, JavaScript, C, C++, and Java by visualizing code】
总结
LEGB查找原则(局部,嵌套,全局,内置作用域)
- Python闭包(def 嵌套一个 def,特点是能记住当时的嵌套作用域)
- Python装饰器(一种特殊的闭包,给多个函数添加相同的功能,如计时功能)
- def语句在这个模块文件顶层将一个函数对象赋值给变量名func
- def是一个简单的可执行语句,可以出现在任意其他语句能够出现的地方,包括嵌套在另一个def之中。
变量的作用域只与定义的位置有关,与函数调用位置无关
LEGB
Local、Enclosing function locals、Global、Built-in
当引用一个变量时,Python按以下顺序依次进行查找:
- 本地作用域 L
- 上层嵌套函数的作用域 E
- 全局作用域 G
- 内置作用域 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
"""