Python类装饰器@typed失效求助:Holding类类型校验异常
你的问题核心在于自定义的__setattr__方法绕过了Python描述符的默认执行流程,导致@typed装饰器配置的类型校验逻辑没有被触发。
为什么会这样?
Python中,当你给实例属性赋值时,默认的__setattr__逻辑是这样的:
- 先检查该属性对应的类属性是否是一个实现了
__set__方法的描述符(比如你的Typed子类) - 如果是描述符,会优先调用描述符的
__set__方法,而不是直接修改实例的__dict__
但你的Holding类重写了__setattr__,直接调用super().__setattr__(name, value)(也就是object类的__setattr__),这会直接把值存入实例的__dict__,完全跳过了描述符的检查和调用流程。所以即使@typed装饰器已经给描述符设置了name属性,它们的__set__方法根本没机会执行,类型校验自然不生效。
另外,你看到的属性值混乱(比如输出里的3.45 2018-01-09 3.45 3.45 11.9025...),是因为在装饰器未正确生效时,描述符的__get__方法里self.name是None,但实例__dict__里没有None这个键——其实是因为当实例__dict__里存在同名属性时,Python会直接返回实例属性,而不会调用描述符的__get__。你之前的赋值操作已经把name、shares等直接存入了实例__dict__,所以描述符的__get__根本没被调用,直到你在main()里手动给描述符设置了name,后续的属性访问才触发了__get__,但此时实例__dict__里的键和描述符的name不匹配,导致取错了值。
如何修复?
修改Holding类的__setattr__方法,在赋值前先检查类中是否存在对应的描述符,如果存在则调用描述符的__set__,否则再执行原有的逻辑:
def __setattr__(self, name, value): if name not in {'name', 'date', 'shares', 'price'}: raise AttributeError(f'No attribute {name}') # 先检查类属性是否是描述符 cls_attr = getattr(type(self), name, None) if isinstance(cls_attr, Typed): cls_attr.__set__(self, value) else: super().__setattr__(name, value)
同时,你会看到装饰器里的print('in typed():', obj.name)正常输出name、shares、price,说明描述符的name属性已经被正确设置。
验证修复效果
修改后运行代码,你会得到符合预期的输出:
in typed(): name in typed(): shares in typed(): price ABC 2018-01-09 500 3.45 1725.0 No attribute time Expected <class 'int'> ABC 2018-01-09 100 3.45 345.0 in main(): name in main(): shares in main(): price
类型校验逻辑正常触发,属性值也不会再混乱了。
内容的提问来源于stack exchange,提问作者Jeremy Chen

