Python 只读包装器 class 拒绝访问某些方法和所有属性

Python read-only wrapper class that denies access to certain methods and all attributes

我有以下基础class。

class BaseWithMethod:
    def __init__(self, prop=None):
        self.prop = prop

    def evil_method(self):
        print(f"%@#&ç? {self.prop}")

我想创建一个包装器 class ReadonlyWrapperSelectedMethods,它显示与基础 class 相同的功能,但不允许某些方法(本例中为 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()

有没有办法在不修改基础class的情况下实现这种行为?请参阅下文,当基础 class 可能更改时如何完成。

尝试 1:修改基数 class

到目前为止,我已经尝试了以下方法。我在基础 class 中添加了一个属性 _initialized 并在 __init__:

末尾将其设置为 True
class BaseWithMethodModified:
    _initialized = False

    def __init__(self, prop=None):
        self.prop = prop
        self._initialized = True

    def evil_method(self):
        print(f"%@#&ç? {self.prop}")

在这种情况下,下面的包装器 class 应该可以完成这项工作。它覆盖 __getattribute__ 方法并将调用委托给超级 class.

允许的方法
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)

这次尝试的问题是我不想修改基础class并且如果属性_initialized在包装器class,它无法访问,因为所有属性访问都委托给基 class 到 __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__returns之后,对象是只读的:

>>> 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>", line 1, in <module>
    readonly_instance.evil_method()
  File "<pyshell#121>", line 10, in __getattribute__
    raise PermissionError()
PermissionError