Python如何对list属性的append操作做校验,避免绕过property setter
关于Python中list类型property属性修改校验的解决方案
问题核心原因
你遇到的问题本质是@property的getter直接返回了内部_cookies列表的引用,调用方拿到引用后直接操作原列表对象,自然会绕开你写在setter里的校验逻辑。
方案1:使用显式的操作方法(更推荐)
符合Python「显式优于隐式」的设计原则,实现简单、易维护,也是大多数业务场景下的最优选择。你不需要完全放弃setter,可以同时保留setter用于批量赋值校验,配合单独的add_cookie方法处理单个添加的场景,同时避免外部直接修改内部列表:
class CookieTray: def __init__(self): self._cookies = [] @property def cookies(self): # 返回不可变的元组,避免外部拿到原列表引用修改 return tuple(self._cookies) @cookies.setter def cookies(self, cookies): if len(cookies) > 8: raise ValueError("Too many cookies in the tray!") self._cookies = list(cookies) def add_cookie(self, cookie_type): if len(self._cookies) >= 8: raise ValueError("Too many cookies in the tray!") self._cookies.append(cookie_type) if __name__ == '__main__': tray = CookieTray() print("Cookies: ", tray.cookies) try: tray.cookies = [1,1,0,0,0,1,1,0,1] # 超过长度,报错 except Exception as e: print(e) tray.cookies = [1,0,1,0,1,0] print(tray.cookies) tray.add_cookie(0) tray.add_cookie(0) try: tray.add_cookie(1) # 超过长度,触发校验报错 except Exception as e: print(e) print(tray.cookies)
该方案优势:
- 逻辑清晰,所有修改内部状态的入口都可控
- 避免意外修改内部属性,封装性更好
- 代码维护成本低,后续新增校验规则只需要修改对应方法即可
方案2:自定义带校验的列表类(需要兼容list操作接口时使用)
如果你需要保留直接对cookies属性调用append、extend等列表方法的使用习惯,可以自定义一个继承自list的子类,重写所有会修改列表的方法,在内部加入校验逻辑:
class LimitedList(list): def __init__(self, max_len, *args, **kwargs): self.max_len = max_len super().__init__(*args, **kwargs) self._check_len() def _check_len(self): if len(self) > self.max_len: raise ValueError(f"List length cannot exceed {self.max_len}") def append(self, *args, **kwargs): super().append(*args, **kwargs) self._check_len() def extend(self, *args, **kwargs): super().extend(*args, **kwargs) self._check_len() def insert(self, *args, **kwargs): super().insert(*args, **kwargs) self._check_len() def __setitem__(self, *args, **kwargs): super().__setitem__(*args, **kwargs) self._check_len() def __iadd__(self, *args, **kwargs): super().__iadd__(*args, **kwargs) self._check_len() return self class CookieTray: def __init__(self): self._cookies = LimitedList(max_len=8) @property def cookies(self): return self._cookies @cookies.setter def cookies(self, cookies): self._cookies = LimitedList(8, cookies) if __name__ == '__main__': tray = CookieTray() print("Cookies: ", tray.cookies) try: tray.cookies = [1,1,0,0,0,1,1,0,1] # 超过长度,报错 except Exception as e: print(e) tray.cookies = [1,0,1,0,1,0] print(tray.cookies) tray.cookies.append(0) tray.cookies.append(0) try: tray.cookies.append(1) # 超过长度,触发校验报错 except Exception as e: print(e) print(tray.cookies)
注意该方案需要你把所有列表的修改方法都重写加校验,避免遗漏,适合需要对外提供兼容list接口的类库场景使用。
内容的提问来源于stack exchange,提问作者fffrost
相关产品推荐
相关产品推荐

