微操征霸
351.42M · 2026-02-04
__call__函数你可以把它理解为:让一个对象 “像函数一样被调用”的魔法入口
class Test:
def __init__(self,name):
self.name = name
print("halo,__init__",self.name)
def __call__(self):
print("halo,__call__",self.name)
t = Test("lh")
上述代码执行结果
发现call函数并没有执行,那我们修改一下代码
class Test:
def __init__(self,name):
self.name = name
print("halo,__init__",self.name)
def __call__(self):
print("halo,__call__",self.name)
t = Test("lh")
t() #只加了一个这个
上述代码执行结果
在介绍call函数之前 我们先了解一下什么是装饰器
def test(name:str)->None:
print("name",name)
test("123")
执行上述代码 我们得到结果
OK!代码很简单 打印出来了我们想要的name。那现在我接着提出我的需求 ,比如我想在打印name前还打印一下当前时间。于是我们改动代码
from datetime import datetime
def test(name:str)->None:
print("当前时间是",datetime.now())
print("name",name)
test("123")
获取结果
也是很简单 。 随便加一个时间就出来了。但是如果有3个方法都需要加当前时间呢?
from datetime import datetime
def test1(name:str)->None:
print("当前时间是",datetime.now())
print("name",name)
def test2(name:str)->None:
print("当前时间是",datetime.now())
print("name",name)
def test3(name:str)->None:
print("当前时间是",datetime.now())
print("name",name)
test1("1")
test2("2")
test3("3")
执行结果
确实能实现我们的需求,但是每个方法都加上重复的代码的快对于后续优化来说也存在一些问题 于是就引了装饰器
from datetime import datetime
def my_timer(func):
def wrapper(*args,**kwargs):
print("当前时间是:",datetime.now())
namer = func(*args,**kwargs)
return namer
return wrapper
@my_timer
def test(name:str)->str:
print("name",name)
return name
test("1")
test("2")
test("3")
获取结果
我们发现在不改变原函数test的情况下,增加了一个@函数,就可以实现刚才的所有逻辑。而@函数也就是我们说的装饰器
看完上述代码。我们应该会有这样的一个疑问就是 @函数的作用是啥,
当程序自上而下执行到第10行时,也就是@函数这一行,其实是会把代码进行一次转换的转换为
整体执行逻辑图就是
┌───────────────────────────────┐
│ 1. 解释器从上到下执行代码 │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ 2. 定义 my_timer(func) │
│ (此时只是把函数对象创建出来)│
└───────────────┬───────────────┘
│
v
┌────────────────────────────────────────────┐
│ 3. 看到 @my_timer │
│ 解释器会先“创建原始 test 函数对象” │
│ original_test = <function test at ...> │
└───────────────┬────────────────────────────┘
│
v
┌────────────────────────────────────────────┐
│ 4. 执行装饰: my_timer(original_test) │
│ - func 形参接到 original_test │
│ - 在 my_timer 内部创建 wrapper │
│ - wrapper 闭包里记住 func=original_test │
│ - return wrapper │
└───────────────┬────────────────────────────┘
│
v
┌────────────────────────────────────────────┐
│ 5. 重新绑定名字: test = wrapper │
│ (从此 test 这个名字不再指向 original_test)│
└────────────────────────────────────────────┘
┌───────────────────────────────┐
│ 运行:调用 test("1") │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ 实际调用的是 wrapper("1") │
│ 因为此时 test 指向 wrapper │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ wrapper 内部: │
│ 1) print 当前时间 datetime.now()│
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ 2) namer = func("1") │
│ 注意:func 是闭包里保存的 │
│ original_test(原 test) │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ original_test 执行: │
│ print("name", "1") │
│ return "1" │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ wrapper 收到返回值 namer="1" │
│ return "1" │
└───────────────────────────────┘
我们写一下同等处理的代码
from datetime import datetime
from functools import wraps
def my_timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("当前时间是:", datetime.now())
return func(*args, **kwargs)
return wrapper
def test(name: str) -> str:
print("name", name)
return name
A = my_timer(test) # A 指向 wrapper
# test 仍然指向原 test
test("1") # 不走时间逻辑(因为调用的是原 test)
A("1") # 走时间逻辑(因为调用的是 wrapper)
发现等号右边接受的wrapper是那个,调用的函数就需要是那个
OK,说完函数装饰器,回到主线这里 ,我们了解call函数是在对象被调用的时候主动执行的,所以当对象(test())在执行的时候是不是也可以加一些自定义逻辑来实现装饰器呢?
from datetime import datetime
class MyTimer:
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print("当前时间是:",datetime.now())
self.func(*args,**kwargs)
print("类装饰器执行结束")
@MyTimer
def test(name):
print("name",name)
test(1)
test(2)
test(3)
执行结果
OK基于上述代码我们看到我们已经实现了一个基本的类装饰器,那参考函数装饰器我们来捋一下类装饰器的执行流程
基于函数装饰器,我们可以得到11行@MyTimer 等同于 test = MyTimer(test)
┌───────────────────────────────┐
│ 1) 解释器创建原 test 函数对象 │
│ original_test = MyTimer(test) │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ 2) 执行 MyTimer(original_test) │
│ - __new__ 创建实例 instance │
│ - __init__(instance, func) │
│ 保存:instance.func = original_test │
└───────────────┬───────────────┘
│
v
┌───────────────────────────────┐
│ 3) 重新绑定:test = instance │
│ 现在 test 不再是函数,而是对象│
└───────────────────────────────┘
test(1)
│
└──► instance.__call__(1)->开始调用call函数
│
├─ print 当前时间
├─ instance.func(1) → 调用原 test(1)
│ └─ print("name", 1)
└─ print("类装饰器执行结束")