问题描述
我有以下基类。
class BaseWithMethod:
def __init__(self,prop=None):
self.prop = prop
def evil_method(self):
print(f"%@#&ç? {self.prop}")
我想创建一个包装类 ReadonlyWrapperSelectedMethods
,它显示与基类相同的功能,但不允许调用某些方法(在此示例中为 evil_method
)。此外,包装的实例应该是只读的,如我的其他 SO 问题 here 中所述。这意味着一旦初始化实例,对 __setattr__
的调用应该会引发错误。以下代码演示了该行为:
# Instantiate the wrapper class
readonly_instance = ReadonlyWrapperSelectedMethods()
# I can access properties
prop = readonly_instance.prop
# This should raise a PermissionError
readonly_instance.prop = 23
# This should also raise a PermissionError
readonly_instance.evil_method()
有没有办法在不修改基类的情况下实现这种行为?当基类可能发生变化时,请参阅下文。
尝试一:修改基类
到目前为止,我已经尝试了以下方法。我向基类添加了一个属性 _initialized
,并在 True
的末尾将其设置为 __init__
:
class BaseWithMethodModified:
_initialized = False
def __init__(self,prop=None):
self.prop = prop
self._initialized = True
def evil_method(self):
print(f"%@#&ç? {self.prop}")
在这种情况下,以下包装类应该完成这项工作。它覆盖 __getattribute__
方法并将调用委托给超类允许的方法。
class ReadonlyWrapperSelectedMethods(BaseWithMethodModified):
"""Read-only wrapper class."""
def __getattribute__(self,name: str):
if "evil" in name:
raise PermissionError()
else:
return super().__getattribute__(name)
def __setattr__(self,key,value) -> None:
if self.__getattribute__("_initialized"):
raise PermissionError()
else:
super().__setattr__(key,value)
这种尝试的问题是我不想修改基类,如果在包装类中定义了属性 _initialized
,则无法访问它,因为所有属性访问通过 __getattribute__
委托给基类。也许这可以通过某种方式规避?
解决方法
不要使用继承,使用组合。利用__slots__
:
class Foo:
def __init__(self,prop=None):
self.prop = prop
def evil_method(self):
print(f"%@#&ç? {self.prop}")
class ReadOnlyWrapper:
__slots__ = ('_foo',)
def __init__(self,foo: Foo):
self._foo = foo
def __getattr__(self,name: str):
if "evil" in name:
raise PermissionError()
else:
return getattr(self._foo,name)
wrapper = ReadOnlyWrapper(Foo())
,
您可以简单地覆盖 __init__
方法:
class ReadonlyWrapperSelectedMethods(BaseWithMethod):
"""Read-only wrapper class."""
def __init__(self,prop=None):
super().__init__(prop)
self._initialized = True
def __getattribute__(self,name: str):
if "evil" in name:
raise PermissionError()
else:
return super().__getattribute__(name)
def __setattr__(self,key,value) -> None:
if hasattr(self,"_initialized"):
raise PermissionError()
else:
super().__setattr__(key,value)
__init__
返回后,对象为只读:
>>> readonly_instance = ReadonlyWrapperSelectedMethods()
>>> vars(readonly_instance)
{'prop': None,'_initialized': True}
>>> prop = readonly_instance.prop
>>> readonly_instance.prop = 23
Traceback (most recent call last):
File "<pyshell#126>",line 1,in <module>
readonly_instance.prop = 23
File "<pyshell#121>",line 16,in __setattr__
raise PermissionError()
PermissionError
>>> readonly_instance.evil_method()
Traceback (most recent call last):
File "<pyshell#127>",in <module>
readonly_instance.evil_method()
File "<pyshell#121>",line 10,in __getattribute__
raise PermissionError()
PermissionError