在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
方法,但如果属性是可变的,则根本无法保护您免受外部修改

显而易见的答案似乎是“好吧,你为什么不寄一份清单?”。但是:

  • 列表可能会变得太大,这个方法会被调用很多次。每次复制列表可能会变得有点慢
  • 如果用户真的想更改日历,返回一份副本会误导h(im/er)认为他们可以更改日历,因为列表的变化不会引发任何异常
  • 我认为,如果用户需要,他们应该花费精力/时间制作副本
  • 我想知道是否有其他方法可以达到同样的效果
  • 另一种可能是制作我自己的
    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]