有時,我們需要原原本本地為對象創建一個副本。舉例來說,假設你想創建一個應用來存儲、
分享、編輯(比如,修改、添加注釋及刪除)食譜。用戶Bob找到一份蛋糕食譜,在做了一些改
變后,覺得自己做的蛋糕非常美味,想要與朋友Alice分享這個食譜。但是該如何分享食譜呢?如
果在與Alice分享之后,Bob想對食譜做進一步的試驗,Alice手里的食譜也能跟着變化嗎?Bob能
夠持有蛋糕食譜的兩個副本嗎?對蛋糕食譜進行的試驗性變更不應該對原本美味蛋糕的食譜造
成影響。
這樣的問題可以通過讓用戶對同一份食譜持有多個獨立的副本來解決。每個副本被稱為一個
克隆,是某個時間點原有對象的一個完全副本。這里時間是一個重要因素。因為它會影響克隆所
包含的內容。例如,如果Bob在對蛋糕食譜做改進以臻完美之前就與Alice分享了,那么Alice就絕
不可能像Bob那樣烘烤出自己的美味蛋糕,只能按照Bob原來找到的食譜烘烤蛋糕。
注意引用與副本之間的區別。如果Bob和Alice持有的是同一個蛋糕食譜對象的兩個引用,那
么Bob對食譜做的任何改變,對於Alice的食譜版本都是可見的,反之亦然。我們想要的是Bob和
Alice各自持有自己的副本,這樣他們可以各自做變更而不會影響對方的食譜。實際上Bob需要蛋
糕食譜的兩個副本:美味版本和試驗版本。
# coding: utf-8 import copy from collections import OrderedDict class Book: def __init__(self, name, authors, price, **rest): '''rest的例子有:出版商,長度,標簽,出版日期''' self.name = name self.authors = authors self.price = price # 單位為美元 self.__dict__.update(rest) def __str__(self): mylist = [] ordered = OrderedDict(sorted(self.__dict__.items())) for i in ordered.keys(): mylist.append('{}: {}'.format(i, ordered[i])) if i == 'price': mylist.append('$') mylist.append('\n') return ''.join(mylist) class Prototype: def __init__(self): self.objects = dict() def register(self, identifier, obj): self.objects[identifier] = obj def unregister(self, identifier): del self.objects[identifier] def clone(self, identifier, **attr): found = self.objects.get(identifier) if not found: raise ValueError('Incorrect object identifier: {}'.format(identifier)) obj = copy.deepcopy(found) obj.__dict__.update(attr) return obj def main(): b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'), price=118, publisher='Prentice Hall', length=228, publication_date='1978-02-22', tags=('C', 'programming', 'algorithms', 'data structures')) prototype = Prototype() cid = 'k&r-first' prototype.register(cid, b1) b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99, length=274, publication_date='1988-04-01', edition=2) for i in (b1, b2): print(i) print('ID b1 : {} != ID b2 : {}'.format(id(b1), id(b2))) if __name__ == '__main__': main()