2014-01-29 55 views
1

有沒有辦法返回實例屬性,但保護它免受修改?我會更具體:返回可變屬性作爲​​Python中的inmutable對象

我有一個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方法,但是如果該屬性是可變的,並不能完全保護您免受外部修改。

顯而易見的答案似乎是「那麼,你爲什麼不返回一份清單的副本?」。但是:

  1. 列表可能會過大,而且這個方法會被調用很多次。每次複製列表可能會變得有點慢。
  2. 如果用戶實際上想要更改日曆,返回副本會誤導h(im/er)認爲他們可以,因爲從列表的突變中不會引發異常。
  3. 我認爲如果需要的話,用戶應該是花費精力/製作副本的人。
  4. 我想知道是否有其他方法可以達到同樣的效果。

另一種可能性是製作我自己的ConstantList並返回。它解決了2和3,但不是1(因爲我不得不將內部可變列表複製到ConstantList)4.另外,對於一個非常簡單的問題,它看起來太多了。

我也知道「我們都同意成年人」,並且過度保護我的實例屬性是不合理的。但我覺得我的好奇心越來越好。

+0

你想達到什麼目的?你說你不想返回一個副本,因爲用戶可能想要修改它,但是你也不想返回真實的東西,因爲用戶可能會修改它。你不能雙方都有。您要麼返回用戶可以修改(並影響底層數據)的內容,要麼不會。 – BrenBarn

+1

您可以返回迭代器而不是列表本身。 –

+0

如果你想要一個不可變的集合,那麼*使用*不可變的連接(例如'tuple')。只需將'get_event_ids'修改爲'返回元組(self._dict [date])'。順便說一句:你應該真的在你的情況下使用['defaultdict(list)'](http://docs.python.org/2/library/collections.html#defaultdict-examples)並避免,這可以讓你避免所有'如果'。 – Bakuriu

回答

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,因此用戶不會無意中改變某些內容,但具有Calendar可以訪問的私人_list屬性來操縱數據。請注意,前導下劃線僅僅是一個約定,並不像C++這樣的語言實際上強制私有範圍。 Here是一個問題,它解釋了範圍更好一點。