Python中父类动态默认参数与super.__init__的行为机制疑问
Python默认参数绑定机制导致的行为差异
问题场景
在Python 3.8/3.9中定义如下父类,试图在实例化时动态设置date属性:
from datetime import datetime, timezone import time class Parent(): def __init__(self, date=datetime.now(timezone.utc).timestamp()): self.date = date def date_id(self): return id(self.date)
子类实例化代码如下(注:此处子类的__int__为笔误,应为__init__):
class Child(Parent): def __init__(self): super().__init__() for i in range(10): time.sleep(1) print(Child().date_id())
运行后发现,连续10次实例化Child,self.date的id完全相同,和预期中每次实例化生成新值的行为不符。
修改父类,将动态赋值移到__init__方法体内后,即可实现预期行为:
class Parent(): def __init__(self): self.date = datetime.now(timezone.utc).timestamp()
原因解析
这是Python的默认参数绑定机制导致的:
- Python中,函数(包括类的
__init__方法)的默认参数值,是在函数定义阶段就计算并绑定的,而非每次调用函数时重新计算。 - 最初的
Parent类中,date=datetime.now(timezone.utc).timestamp()这个默认参数,在Parent类被定义的那一刻就执行了一次,得到一个固定的时间戳数值,之后所有不传date参数的__init__调用,都会复用这个预先计算好的固定值。 - 因为每次实例化都使用同一个数值,所以
self.date指向的是同一个对象(Python对小数值存在对象复用优化,但核心原因还是默认参数仅计算一次),因此id()返回的结果完全相同。 - 而将赋值逻辑移到
__init__方法体内后,每次实例化调用__init__时,都会重新执行datetime.now(timezone.utc).timestamp()生成新的时间戳,自然会得到不同的对象引用,id()结果也就不同了。
内容的提问来源于stack exchange,提问作者Teddy
相关产品推荐
相关产品推荐

