Можно ли ограничить использование метода сеттера / модификатора только из другого класса? - PullRequest
1 голос
/ 23 января 2020

Я реализую Связанный список, и у меня есть два класса, один для списка и другой для ссылок. У объекта ссылки есть атрибут экземпляра self.__next, который является закрытым. Я хочу, чтобы только объект LinkedList мог изменять значение этого атрибута, он не должен изменяться напрямую с помощью объекта Link.

Если это возможно, я предполагаю, что его нужно будет реализовать с помощью абстрактный класс и / или наследование.

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

1 Ответ

1 голос
/ 24 января 2020

Хорошо. Прежде чем мы решим вашу проблему, давайте выясним, есть ли у python модификаторы доступа, такие как private.

Спецификация доступа определяется соглашениями:

  • A одиночное подчеркивание перед именем переменной будет означать, что переменная используется для некоторых внутренних логик c внутри класса.

  • Два подчеркивания перед именем переменной в идеале означают, что переменная должна быть закрытой, но это не так.

>>> 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 иметь возможность изменять link член экземпляра, вы можно проверить, откуда вызывается метод установки с помощью модуля 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
...