是否可以限制仅从不同的 class 使用 setter/modifier 方法?

Is it possible to restrict the use of a setter/modifier method from a different class only?

我正在实施一个 Linked List,我有两个 classes,一个用于列表,另一个用于 links。 link 对象有一个私有的实例属性 self.__next。我只希望 LinkedList 对象能够修改此属性的值,不应直接使用 Link 对象进行更改。

如果可能的话,我猜它必须通过抽象 class and/or 继承来实现。

class Link:
    def __init__(self):
        self.__next = True

    def setter(self):
        self.__next = False


class LinkedList():
    def setter(self):
        """Changes the value of 'self.__next' in the link"""
        pass

好的。在我们解决您手头的问题之前,让我们看看 python 是否真的有像 private.

这样的访问修饰符

访问规范按约定完成:

  • 变量名前的单个下划线表示该变量用于 class 中的某些内部逻辑。

  • 变量名前的两个下划线理想情况下表示该变量应该是私有的,但事实并非如此。

>>> class Foo:
...     def __init__(self):
...         self.x = 10    # public
...         self._x = 20   # internal
...         self.__x = 30  # private
...
>>> f = Foo()
>>> f.x
10
>>> f._x
20
>>> f.__x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__x'

您可能会认为 __x 无法访问,因为它是私有的。但是,

>>> f._Foo__x
30
>>> dir(f)
['_Foo__x', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_x', 'x']

dir(f)的第一个值是_Foo__x,这就是变量__x。 Python 只是重命名名称前带有 __(2 个下划线)的变量。

但是,如果你真的想阻止Link class对象能够修改link实例成员,你可以检查setter方法在哪里使用 inspect 模块调用。

import inspect


class Link:

    def __init__(self, value=None, link=None):
        self.value = value
        self._link = link

    @property
    def link(self):
        return self._link

    @link.setter
    def link(self, value):
        caller = inspect.stack()[1].function
        if hasattr(LinkedList, caller):
            self._link = value
        else:
            raise AttributeError("Can't set value of link from class Link")


class LinkedList:

    def __init__(self):
        self.head = None

    def append_link(self, value):
        if not self.head:
            self.head = Link(value)
        else:
            t = self.head
            while t.link is not None:
                t = t.link
            t.link = Link(value)

    def __repr__(self):
        t = self.head
        list_values = []
        while t != None:
            list_values.append(str(t.value))
            t = t.link
        return f'[{", ".join(list_values)}]'


ll = LinkedList()
print(ll)
ll.append_link(10)
ll.append_link(20)
ll.append_link(30)
ll.append_link(40)
print(ll)

l = Link()
l.link = 'value'

输出:

$ python LinkedList.py
[]
[10, 20, 30, 40]
Traceback (most recent call last):
  File "LinkedList.py", line 55, in <module>
    l.link = 'value'
  File "LinkedList.py", line 20, in link
    raise AttributeError("Can't set value of link from class Link")
AttributeError: Can't set value of link from class Link