函数表达式可通过默认参数、装饰器或闭包实现内部自引用,核心是避免依赖全局名或运行时查找;默认参数法最稳妥,装饰器法更清晰,闭包法适用于简单场景。

函数表达式本身没有名字,所以不能像具名函数那样直接用函数名在内部引用自己。但有几种可靠方式可以实现“在函数表达式内部拿到自身引用”,关键在于**避免依赖全局变量名或运行时查找**,确保即使函数被重命名、赋值给新变量或从命名空间中删除,仍能正确引用自身。
用默认参数实现早期绑定
这是最常用、最稳妥的方式:在定义函数表达式时,把函数自身设为一个带默认值的参数(通常放在最后),并在函数体内部通过该参数访问自己。
- 定义时先写函数,再补默认值(因为函数名在定义完成前不可用)
- 利用 Python 默认参数在定义时求值的特性,完成“早期绑定”
- 示例:
def countdown(n, self=None):
if self is None:
self = countdown # 回退到全局名(仅作保底)
print(n)
if n > 0:
self(n - 1) # 用 self 调用自身
<h1>设置默认参数,绑定当前函数对象</h1><p>countdown.<strong>defaults</strong> = (countdown,) # 注意:Python 3.8+ 推荐用 <strong>defaults</strong>,旧版用 func_defaults
countdown(3) # 输出 3 2 1 0
</p>用装饰器自动注入 self 参数
把上述逻辑封装成装饰器,让用法更清晰、安全:
- 装饰器在函数定义后立即修改其默认参数,注入自身引用
- 调用时无需传 self,它已作为隐式参数就位
- 示例:
from functools import wraps <p>def self_ref(f): @wraps(f) def wrapper(*args, **kwargs):</p><h1>将函数自身插入到 args 开头(或按需插入末尾)</h1><pre class="brush:php;toolbar:false;"> return f(f, *args, **kwargs) return wrapper
@self_ref def factorial(self, n): return 1 if n
print(factorial(5)) # 输出 120
用闭包 + lambda(适用于简单场景)
如果函数逻辑很短,可借助闭包提前捕获函数对象:
- 先定义函数对象,再用 lambda 或普通函数包装,内部直接使用该变量
- 注意:此方式要求变量名在作用域中稳定存在(适合局部使用)
- 示例:
make_counter = lambda: (lambda self, count=0: (
lambda: (print(count), self(self, count + 1))[0]
))(lambda s, c: (lambda: (print(c), s(s, c + 1))[0]))
<h1>更易读的等价写法:</h1><p>def make_counter():
def counter(self, n=0):
print(n)
if n </p><p>cnt = make_counter()
cnt() # 输出 0 1 2 3
</p>不推荐的方式:globals() 查找或 arguments.callee 类比
Python 没有 arguments.callee,也不建议在函数体内用 globals().get('func_name') 来查自己:
- 一旦函数被重命名或删除,
globals()查找就会失败 - 破坏封装性,依赖外部命名,不符合“表达式自身可移植”的初衷
- 性能略差,每次调用都触发字典查找











