在Python中将可变属性作为inmutable对象返回
有没有一种方法可以返回实例属性,但防止修改?我将更具体地说: 我有一个在Python中将可变属性作为inmutable对象返回,python,python-3.x,Python,Python 3.x,有没有一种方法可以返回实例属性,但防止修改?我将更具体地说: 我有一个Calendar类,它使用一个内部字典,由datetime.date对象索引,并将事件ID列表作为值(实际上是ints,但这与此无关) 正如你所看到的,这很简单。我只想封装列表创建和事件ID提取逻辑。但是我想保护这个日历不被无意的修改。更具体地说,不知情的用户实际上可以这样做: calendar = Calendar() calendar.add_event(some_date, some_event) event_ids =
Calendar
类,它使用一个内部字典,由datetime.date
对象索引,并将事件ID列表作为值(实际上是int
s,但这与此无关)
正如你所看到的,这很简单。我只想封装列表创建和事件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
我知道这个例子似乎有点特别,但如果你仔细想想,用户可能希望以某种方式修改日历返回的列表,为什么他希望这样做会从内部修改日历
另一件(类似的)事情是当我想要设置只读属性时。python的方法是使用属性
,而不使用setter
方法,但如果属性是可变的,则根本无法保护您免受外部修改
显而易见的答案似乎是“好吧,你为什么不寄一份清单?”。但是:
ConstantList
并返回该列表。它解2和3,但不是1(因为我必须将内部可变列表复制到ConstantList
),也不是4。而且,对于一个非常简单的问题来说,似乎工作量太大了
我还知道“我们都是同意的成年人”,过度保护我的实例属性是不道德的。但是我想我的好奇心战胜了我。这是创建某种不可变列表对象的唯一方法,但是你可以这样做,这样它就能解决你所有的问题,例如:
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,这样用户就不会意外地更改某些内容,但它有一个private\u list
属性,Calendar
可以访问该属性来操作数据。请注意,主要下划线只是一个约定,实际上并没有像C++那样使用私有的范围。这是一个可以更好地解释范围的问题。您到底想要实现什么?您说您不想返回副本,因为用户可能想修改它,但您也不想返回真实的副本,因为用户可能会修改它。你不能两全其美。您可以返回用户可以修改(并影响基础数据)的内容,也可以不返回。您可以返回迭代器,而不是列表本身。如果需要不可变的集合,请使用不可变的连接(例如,元组
)。只需将get\u event\u id
修改为返回元组(self.\u dict[date])
。顺便说一句:你真的应该在你的案例中使用a和avoid,这样你就可以避免所有的if
s。你的英语肯定高于平均水平,IMHO.)@但是从列表中创建一个新的元组所用的时间与复制元组所用的时间相同(或更多)?无论如何,感谢您提供的defaultdict
提示!
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]