⚠️ 请确保CPython的版本不低于3.8,因语法涉及2个PEP:PEP570,PEP572
问题
启发自这篇问答:Is there a generic way for a function to reference itself?
曾有一个提议PEP 3130 – Access to Current Module/Class/Function,但是因为方案不完善而被拒绝了
就像__class__
在函数体内指向于调用这个函数的类,是否有一个指针或者说魔法关键词,例如__func__
,在函数体内使用时,能指向被调用的函数本身?因此,我可以写出像下面这样的代码:
1 | def foo(): |
解决方案
暂时还没有这样的一个指针和魔法关键词,起码就python
3.8
来看。因此暂时只能用一些侵入式的方案,来模拟实现这一需求。
通过调用栈信息来获取函数引用
我可以在一个函数的函数体内访问这个函数本身,通过以下代码:
1 | def foo(): |
因为以上方式需要显式地指定函数名,当函数名发生改变时,函数体内相应的名字也要进行改变。为了避免这一点,需要函数有充足的信息以定位自身。例如,当可以通过函数在定义时的名字在相应的全局命名空间中查找引用时
这个名字存储于function
.__code__.co_name
:如果用def func_name
或者async def func_name
创建函数,func_name
就是名字;如果用lambda
创建函数,名字就是<lambda>
,我特别不建议如此。
⚠️ 这个属性co_name
是只读的且不能被改变。但你可以自己创建一个新的code对象,复制相同的功能,选用不同的名字。
⚠️ 这里并没有用到function.__name__
,这个属性是可变的,而且更加灵活和合适,但是相对来说获取困难,所以不采用。特别是,function.__qualname__
也同样难以获得,不然可以统一地解决很多问题了。
Note Name of a Python function
,就可以使用以下帮助函数来获取这个函数的引用:
1 | import sys |
See Also
- http://code.activestate.com/recipes/66062/
- https://stackoverflow.com/questions/33162319/how-can-get-current-function-name-inside-that-function-in-python
- https://stackoverflow.com/questions/21218416/reference-to-the-function-from-inside-itself-like-arguments-callee-in-javascrip
- https://stackoverflow.com/questions/4492559/how-to-get-current-function-into-a-variable
- https://stackoverflow.com/questions/5067604/determine-function-name-from-within-that-function-without-using-traceback
- …
测试结果如下:
1 | >>> def foo(): return getfuncref() |
Note 截至python
3.8
,调用栈的帧frame(frame-objects)上并没有对函数对象的引用,只是引用了函数相应的code对象,以及相关的执行环境信息。因此,通过审查调用栈来获取函数function调用时对应的帧frame的信息,以获取function的引用,需要根据一些信息以间接方式在相关的命名空间上搜索。而且由于code对象上没有存储函数的描述信息(尽管这种策略有利于code对象的复用),如果函数定义在类中,那么定位这个函数的策略将会比较复杂,因为有一些不同的情况需要分别对待,而且可能还需要一些另外的信息。
🔔 我觉得你可以利用inspect模块来辅助收集信息。
通过把函数的引用绑定到参数上
可以通过在调用函数时传入这个函数的引用,在函数体内使用:
1 | def foo(__func__, /): |
为此,我设计了3个装饰器来实现一种绑定效果
1 | from functools import partial, update_wrapper |
测试结果如下:
1 | >>> def foo(__func__, /, a, *b, c, **d): pass |