如何在不修改基类的前提下实现禁止特定方法调用的Python只读包装类
无需修改基类的解决方案
当然可以做到!不用修改基类也能实现你想要的只读包装+禁止特定方法的需求,下面给你两种靠谱的方案:
方案1:组合模式(推荐)
这种方式不需要继承基类,而是把基类实例作为包装类的内部属性,完全掌控所有访问逻辑,灵活性拉满:
class BaseWithMethod: def __init__(self, prop=None): self.prop = prop def evil_method(self): print(f"%@#&ç? {self.prop}") class ReadonlyWrapperSelectedMethods: """Read-only wrapper that blocks specific methods.""" def __init__(self, prop=None): # 初始化内部的基类实例 self._base_instance = BaseWithMethod(prop) # 标记初始化完成,用来控制只读状态 self._initialized = True def __getattr__(self, name: str): # 当访问的属性/方法不在包装类自身时,转发给基类实例 if "evil" in name: raise PermissionError("Cannot call evil methods") # 获取基类的属性或方法并返回 return getattr(self._base_instance, name) def __setattr__(self, key, value): # 初始化阶段允许设置内部属性(_base_instance和_initialized) if not hasattr(self, "_initialized"): super().__setattr__(key, value) else: raise PermissionError("Cannot modify readonly instance")
测试效果:
# 实例化包装类 readonly_instance = ReadonlyWrapperSelectedMethods() # 正常访问属性 prop = readonly_instance.prop print(prop) # 输出: None # 赋值操作抛出PermissionError try: readonly_instance.prop = 23 except PermissionError as e: print(e) # 输出: Cannot modify readonly instance # 调用禁止方法抛出PermissionError try: readonly_instance.evil_method() except PermissionError as e: print(e) # 输出: Cannot call evil methods
这种方式的优势是完全隔离了包装类和基类,所有控制逻辑都在包装类内部,不用动基类的任何代码,逻辑清晰还不容易出问题。
方案2:继承基类并调整__getattribute__逻辑
如果一定要用继承的方式,我们可以在__getattribute__里优先处理包装类自身的属性,再委托给基类:
class BaseWithMethod: def __init__(self, prop=None): self.prop = prop def evil_method(self): print(f"%@#&ç? {self.prop}") class ReadonlyWrapperSelectedMethods(BaseWithMethod): """Read-only wrapper class (inheritance version).""" def __init__(self, prop=None): # 先标记未初始化,再调用基类构造方法 self._initialized = False super().__init__(prop) # 初始化完成后标记为True self._initialized = True def __getattribute__(self, name: str): # 优先获取包装类自身的属性(比如_initialized) if name == "_initialized": return object.__getattribute__(self, name) # 禁止调用包含"evil"的方法 if "evil" in name: raise PermissionError("Cannot call evil methods") # 其他属性/方法委托给基类处理 return super().__getattribute__(name) def __setattr__(self, key, value): # 未初始化阶段允许设置属性(包括基类的prop) if not object.__getattribute__(self, "_initialized"): super().__setattr__(key, value) else: raise PermissionError("Cannot modify readonly instance")
测试效果和方案1完全一致,这里的关键是用object.__getattribute__(self, name)直接获取包装类自身的属性,避免被super().__getattribute__转发到基类查找,这样就能正确访问包装类自己的_initialized属性了。
为什么你之前的方案有问题?
你之前的尝试中,包装类继承了修改后的基类,但如果不修改基类,包装类自己的_initialized属性会被super().__getattribute__转发到基类查找,而基类没有这个属性,自然会出问题。上面的两种方案,要么用组合完全分离(方案1),要么优先处理自身属性(方案2),完美解决了这个痛点。
内容的提问来源于stack exchange,提问作者Christian Baumann
相关产品推荐
相关产品推荐

