Okay. Before we tackle your problem at hand, let's find out if python actually has access modifiers like private
.
Access specification is done by conventions:
A single underscore before a variable name would mean that the variable is used for some internal logic inside the class.
Two underscores before a variable name would ideally mean that the variable is meant to be private, however it's not.
>>> 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'
You might be tempted to think that __x
can't be accessed because it is private. But,
>>> 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']
The first value of dir(f)
is _Foo__x
, this is the variable __x
. Python simply renames variables that have __
(2 underscores) before their names.
However, if you really want to prevent Link
class objects from being able to modify link
instance member, you can check where the setter method is being called from using the inspect
module.
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'
Output:
$ 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