有没有办法返回实例属性但保护它不被修改?我会更具体:
我有一个Calendar
类,它使用由datetime.date
个对象索引的内部字典,以及事件ID列表作为值(实际上int
s,但这是无关紧要的。)
class Calendar:
def __init__(self):
self._dict = {}
def add_event(self, date, event):
if date in self._dict:
self._dict[date].append(event.id)
else:
self._dict[date] = [event.id]
def get_event_ids(self, date):
if date in self._dict:
return self._dict[date]
else:
return []
如您所见,它非常简单。我只想封装列表创建和事件ID提取逻辑。但我想保护这个日历免受无意的修改。更具体地说,一个不知情的用户实际上可以这样做:
calendar = Calendar()
calendar.add_event(some_date, some_event)
event_ids = calendar.get_event_ids(some_date)
event_ids[0] = 42 # modifies the internal list in the calendar
我知道这个例子似乎有点特别,但如果你考虑一下,用户可能想以某种方式修改日历返回的列表,为什么他希望这样做会修改从里面的日历?
另一个(类似的)事情是我想设置一个只读属性。 pythonic方法是使用property
而不使用setter
方法,但如果属性是可变的,则根本不能保护您免受外部修改。
显而易见的答案似乎是“好吧,你为什么不归还一份清单?”。但是:
另一种可能性是制作我自己的ConstantList
并返回。它解决了2和3,但不是1(因为我不得不将内部的,可变列表复制到ConstantList
)4。此外,对于一个非常简单的问题,它看起来似乎太多了。
我也知道“我们都同意成年人”,并且过度保护我的实例属性是unpythonic。但我认为我的好奇心对我有好处。
答案 0 :(得分:1)
创建某种不可变列表对象的唯一方法,但你可以这样做,以便它解决你所有的问题,例如:
class PrivateList(Sequence):
def __init__(self):
self._list = []
def __getitem__(self, index):
return self._list[index]
def __len__(self):
return len(self._list)
class Calendar:
def __init__(self):
self._dict = defaultdict(PrivateList)
def add_event(self, date, event):
self._dict[date]._list.append(event.id)
def get_event_ids(self, date):
return self._dict[date]
PrivateList
现在公开了一个类似元组的API,因此用户不会意外地更改某些内容,但具有_list
个Calendar
属性,{{1}}可以访问该属性来操纵数据。请注意,前导下划线只是一种约定,并不像c ++这样的语言实际强制执行私有作用域。 Here是一个可以更好地解释范围的问题。
答案 1 :(得分:0)
另一种可能性是制作我自己的 ImmutableList
并返回它。它可以解决 2 和 3,但不能解决 1(因为我必须将内部可变列表复制到 ImmutableList
),也不能 4。
在不可变列表(例如Calendar
)的初始化期间您不必复制tuple
可变列表:您可以通过存储一个ImmutableList >引用到 Calendar
可变列表(这是 Proxy pattern 的示例)。这与 @aquavitae’s solution 不同,后者将可变列表存储为 ImmutableList
的私有属性,并从 Calendar
对其进行变异,从而危及 encapsulation:
import collections
class ImmutableList(collections.abc.Sequence):
def __init__(self, list):
self._list = list
def __getitem__(self, index):
return self._list[index]
def __len__(self):
return len(self._list)
class Calendar:
def __init__(self):
self._dict = collections.defaultdict(list)
def add_event(self, date, event):
self._dict[date].append(event.id)
def get_event_ids(self, date):
return ImmutableList(self._dict[date])