PyTorch中nn.Module子类属性式子模块赋值失效问题
问题分析与解决方案
你的问题并不是nn.Module的属性机制损坏,而是它重写的__setattr__方法为了管理子模块、参数和缓冲区,行为和普通Python类不同,导致你的@property setter没有按预期工作。
原因解释
nn.Module的__setattr__会根据赋值对象的类型和属性名做特殊处理:
- 当赋值的是
nn.Module或Parameter实例时,若属性名不以下划线开头,会将其注册到模块的_modules或_parameters字典中,而非直接存入实例的__dict__; - 若属性名以下划线开头,新版本PyTorch会直接存入
__dict__,但旧版本可能仍会将其注册到_modules。
你的代码中,_submodule以下划线开头,在setter中执行self._submodule = new_submodule时,触发了nn.Module的__setattr__,导致赋值行为没有正确修改到你期望的属性存储位置,最终getter返回的还是旧值。
解决方案
有几种简单的方法可以解决这个问题:
方法1:直接操作实例的__dict__绕过__setattr__
修改setter方法,直接修改实例字典中的值,避免触发nn.Module的特殊处理:
@x.setter def x(self, new_submodule): self.__dict__['_submodule'] = new_submodule
方法2:使用非下划线开头的属性名
将存储子模块的属性名改为普通名称(不带下划线前缀),让nn.Module正常管理子模块的注册与替换:
class MyModule(nn.Module): def __init__(self): super().__init__() self.submodule = nn.Linear(5, 5) @property def x(self): return self.submodule @x.setter def x(self, new_submodule): self.submodule = new_submodule
方法3:手动注册子模块(可选)
如果需要保留下划线前缀的属性名,可以在setter中手动调用register_module方法,确保子模块被正确更新:
@x.setter def x(self, new_submodule): self.register_module('_submodule', new_submodule)
验证修改效果
无论使用哪种方法,重新运行代码后,赋值操作都会生效,输出会变为:
Initial module.x: Linear(in_features=5, out_features=5, bias=True) After assignment attempt, module.x: Linear(in_features=5, out_features=2, bias=True)
内容的提问来源于stack exchange,提问作者user118967
相关产品推荐
相关产品推荐

