2015年12月27日 星期日

[GNU Make] 命令 : 空命令 & 命令環境

空命令 
空命令 (empty command) 就是一個什麼事都不做的命令. 例如: 
先前有提到工作目標的必要條件可以跟著一個分號和命令. 此處分號之後空無一物, 這表示命令不存在. 當然你也可以在工作目標後指定只包含一個 Tab 的空白列, 不過這樣做不利於閱讀與維護. 空命令最常用來避免一個樣式規則來比對特定工作目標, 進而執行你不想要的命令. 請注意的是, 在 make 其它的版本中, 空工作目標 (empty target) 有時候會被當作假工作目標 (phony target) 來用. 在 GNU make 中, 你可以使用 .PHONY 這個特殊工作目標: 
A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance.

來避免將工作目標視為檔案, 而造成不預期的執行結果. 

命令環境 
make 執行命令時會從 make 本身繼承其處理環境. 此環境包括當前的工作目標, 檔案描述符以及傳遞自 make 的環境變數. 當一個 subshell 被建立時, make 會將若干變數加入環境: 
* MAKEFLAGS
* MFLAGS
* MAKELEVEL

MAKEFLAGS 變數包含了你傳遞給 make 的命令列選項; MFLAGS 變數的內容是 MAKEFLAGS 的鏡射版本, 存在的理由是為了舊版的相容性; MAKELEVEL 變數的內容代表 make 巢狀調用的次數. 也就是說當 make 遞迴調用 make 時, MAKELEVEL 變數的值就會加一. 對 make 而言, 具單一父程式的子程式將會具有一個同一值的 MAKELEVEL 變數. 這些變數通常會被用來管理遞迴建造 (recursive make). 

當然使用者可以透過 export 指令 將任何變數加入子城市的環境之中. 

make 用來執行命令的當前工作目錄就是上層 make 的工作目錄. 這個目錄通常就是你用來執行 make 程式的目錄, 不過你可以透過 --directory ( -C) 參數加以變更: 
  1. -C DIRECTORY, --directory=DIRECTORY  
  2.                             Change to DIRECTORY before doing anything.  
請注意! 僅使用 --file 來指定不同的 makefile 並無法變更當前目錄, 這只會影響所讀取的 makefile: 
  1. -f FILE, --file=FILE, --makefile=FILE  
  2.                             Read FILE as a makefile.  
make 所衍生 (spawn) 的每個子行程都會繼成三個標準的擋案描述符: stdin, stdout 和 stderr. 這並沒有任何值得特別注意的地方, 除了命令稿可以讀取它的 stdin 這件事. 這是合理且可行的. 一旦命令稿完成它的剖析動作後, 接下來命令便會如剖析結果被執行. 不過使用者一般不會透過這種互動方式來執行 makefile. 使用者通常會希望這麼做: 
- 啟動 make
- "走完" 每個步驟
- 傳回稍後要檢查的結果.

當然能夠讀取 stdin 將有助於與 "cron 為基礎的自動建奏程序" 的互動. 而 makefile 中常見的一個錯誤就是意外的讀取了 stdin: 
  1. $(DATA_FILE): $(RAW_DATA)  
  2.     grep pattern $(RAW_DATA_FILES) > $@  
此處當我們以變數來作為 grep 命令 指定輸入檔的時候, 如果誤用了變數名稱導致展開來後空無一物, grep 命令 將會讀取自 stdin! 但這不會顯示提式符或指示為何 make "掛掉了". 這個問題的一個簡單解決方案就是總在命令列上使用 /dev/null 以作為額外的 "檔案": 
  1. $(DATA_FILE): $(RAW_DATA)  
  2.     grep pattern $(RAW_DATA_FILES) /dev/null > $@  
現在這個 grep 命令 一定不會去讀取 stdin!

[ Python 考題 ] 華為面試題 - 求兩序列的和最小差值序列

Source From Here
題目:
有兩個序列 ab,大小都為 n,序列元素的值任意整形數,無序;要求:通過交換 a,b 中的元素,使 [序列 a 元素的和] 與 [序列 b 元素的和] 之間的差最小。

方法:
可能之一的演算法如下:
考慮已經 ab 排序過後的結果存放於序列 c, 將其元素一一分配到 de 序列使其兩個序列的合的差值為最小:
1. 依序從序列 c 取出兩個元素
2. 計算目前 de 序列中元素的合 d_sum 與 e_sum.
3. 如果 d_sum > e_sum, 則將取出的第一個元素放到 d 序列, 第二個元素加到 e 序列; 反之則將第一個元素加到 e 序列, 第二個元素加到 d 序列.
4. 反覆步驟 1-3 直到 c 序列所有元素都分配到de 序列。

範例代碼如下:
  1. #!/usr/bin/env python  
  2. tests = [[1,2,3,4,5,6,700,800],  
  3.          [10001,10000,100,90,50,1],  
  4.          range(111),  
  5.          [123121231123221030293211]]  
  6.   
  7. for c in tests:  
  8.     c.sort()  
  9.     a = []  
  10.     b = []  
  11.     # Approach 1  
  12.     #for i in range(len(c)/2):  
  13.     #    if (i%2) == 0:  
  14.     #        a.append(c[i*2])  
  15.     #        b.append(c[i*2+1])  
  16.     #    else:  
  17.     #        b.append(c[i*2])  
  18.     #        a.append(c[i*2+1])  
  19.   
  20.     # Approach 2  
  21.     for i in range(len(c)/2):  
  22.         a_sum = reduce(lambda x,y:x+y, a, 0)  
  23.         b_sum = reduce(lambda x,y:x+y, b, 0)  
  24.         if a_sum > b_sum:  
  25.             a.append(c[i*2])  
  26.             b.append(c[i*2+1])  
  27.         else:  
  28.             b.append(c[i*2])  
  29.             a.append(c[i*2+1])  
  30.   
  31.     print("C= %s" % c)  
  32.     print("A= %s" % a)  
  33.     print("B= %s" % b)  
  34.   
  35.     a_sum = reduce(lambda x,y:x+y, a)  
  36.     b_sum = reduce(lambda x,y:x+y, b)  
  37.     print("A sum to %d; B sum to %d; Diff=%d" % (a_sum, b_sum, abs(a_sum-b_sum)))  
執行結果如下:
# ./intv_q1.py
C= [1, 2, 3, 4, 5, 6, 700, 800]
A= [2, 3, 6, 700]
B= [1, 4, 5, 800]
A sum to 711; B sum to 810; Diff=99
========================
C= [1, 50, 90, 100, 10000, 10001]
A= [50, 90, 10000]
B= [1, 100, 10001]
A sum to 10140; B sum to 10102; Diff=38
========================
C= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
A= [2, 3, 6, 7, 10]
B= [1, 4, 5, 8, 9]
A sum to 28; B sum to 27; Diff=1
========================
C= [1, 1, 2, 3, 29, 30, 210, 232, 12311, 12312]
A= [1, 3, 29, 232, 12311]
B= [1, 2, 30, 210, 12312]
A sum to 12576; B sum to 12555; Diff=21
========================

This message was edited 5 times. Last update was at 28/12/2015 10:23:41

2015年12月25日 星期五

[ Python 常見問題 ] 如何用 Python 來進行查詢和替換一個文本字符串?

Source From Here
可以使用 sub() 方法來進行查詢和替換,sub 方法的格式為:
re.sub(patternreplstringcount=0, flags=0)
Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl. If the pattern isn’t found, string is returned unchanged. repl can be a string or a function; if it is a string, any backslash escapes in it are processed. That is, \n is converted to a single newline character, \r is converted to a carriage return, and so forth. Unknown escapes such as \j are left alone. Backreferences, such as \6, are replaced with the substring matched by group 6 in the pattern. For example:
>>> import re
>>> re.sub(r'(blue|white|red)', 'colour', 'blue socks and red shoes')
'colour socks and colour shoes'

If repl is a function, it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string. For example:
>>> def colorRepl(matchObj):
... if matchObj.group(0) == 'blue': return 'bluest'
... elif matchObj.group(0) == 'red': return 'redest'
... else: return 'colour'
...
>>> re.sub(r'(blue|white|red)', colorRepl, 'blue sock, red shoes, white cloth and orange cap')
'bluest sock, redest shoes, colour cloth and orange cap'

subn() 方法執行的效果跟 sub() 一樣,不過它會返回一個二維數組,包括替換後的新的字符串和總共替換的數量:
re.subn(pattern, repl, string, count=0, flags=0)
Perform the same operation as sub(), but return a tuple (new_string, number_of_subs_made). For example:
>>> re.subn(r'(blue|white|red)', 'colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> re.subn(r'(blue|white|red)', 'colour', 'blue socks and red shoes', count=1)
('colour socks and red shoes', 1)


[ Python 文章收集 ] python實踐設計模式(二)Builder,Singleton,Prototype

Source From Here 
4. Builder模式 
個人理解,如果說工廠模式旨在選擇創建哪一類的實例,而 Builder 模式的重點是封裝一個實例的複雜創建過程。它可以將一個產品的內部表象與產品的生成過程分割開來,從而可以使一個建造過程生成具有不同的內部表象的產品對象。也就是說,建造的步驟可以穩定不變,但是每一步的內部表象可以靈活變化。UML 圖如下: 
Builder: 為創建 Product 對象的各個部件指定抽象接口,python中為父類。
ConcreteBuilder: 實現 Builder 的接口以構造和裝配該產品的各個部件,定義並明確它所創建的表示,並提供一個檢索產品的接口,也就是返回產品類的方法。
Director: 構造一個使用Builer接口的對象,該對像中定義了建造對象的步驟順序。
Product: 表示被構造的複雜對象。ConcreteBuilder創建該產品的內部表示並定義它的具體裝配方法,包含定義組成部件的類,以及將這些部件裝配成最終產品的接口。

一個比較貼切的例子: 
要建一座房子,可是我不知道怎麼蓋,於是我需要找建築隊的工人他們會,還得找個設計師,他知道怎麼設計,我還要確保建築隊的工人聽設計師的領導,而設計師本身不干活,只下命令,這裡砌一堵牆,這裡砌一扇門,這樣建築隊的工人開始建設,最後,我可以向建築隊的工人要房子了。在這個過程中,設計師是什麼也沒有,除了他在腦子裡的設計和命令,所以要房子也是跟建築隊的工人要。在這個例子中 Director 是設計師,Builder 代表建築隊工人會的建築技能,ConcreteBuilder 工人建築技能的具體操作,Product就是我要蓋的房子。

下面代碼的例子,建築隊的工人有砌牆,裝窗戶,裝門的技能以及交房的安排,設計師決定了建設房屋的安排和步驟,現在我要通過2個建築隊的民工,建2所房子,實例代碼如下: 
- Builder_test.py 
  1. #!/usr/bin/env python  
  2. class Builder:  
  3.     def BuildWall(self):  
  4.         pass  
  5.     def BuildDoor(self):  
  6.         pass  
  7.     def BuildWindow(self):  
  8.         pass  
  9.     def GetRoom(self):  
  10.         pass  
  11. class ConcreteBuilder1(Builder):  
  12.     def __init__ (self):  
  13.         self.__Room= []  
  14.     def BuildWall(self):  
  15.         self.__Room.append( " Builder1 Build the wall. " )  
  16.     def BuildDoor(self):  
  17.         self.__Room.append( " Builder1 Build the door. " )  
  18.     def BuildWindow(self):  
  19.         self.__Room.append( " Builder1 Build the window. " )  
  20.     def GetRoom(self):  
  21.         return self.__Room  
  22. class ConcreteBuilder2(Builder):  
  23.     def __init__( self):  
  24.         self.__Room= []  
  25.     def BuildWall(self):  
  26.         self.__Room.append( " Builder2 Build the wall. " )  
  27.     def BuildDoor(self):  
  28.         self.__Room.append( " Builder2 Build the door. " )  
  29.     def BuildWindow(self):  
  30.         self.__Room.append( " Builder2 Build the window. " )  
  31.     def GetRoom(self):  
  32.         return self.__Room  
  33. class Director:  
  34.     def __init__(self,Builder) :  
  35.         self.__build= Builder  
  36.     def order(self):  
  37.         self.__build.BuildWall()  
  38.         self.__build.BuildWindow()  
  39.         self.__build.BuildDoor()  
  40. if __name__ == "__main__" :  
  41.   
  42.     builder1 = ConcreteBuilder1()  
  43.     director= Director(builder1)  
  44.     director.order()  
  45.     print builder1.GetRoom()  
  46.   
  47.     builder2= ConcreteBuilder2()  
  48.     director= Director(builder2)  
  49.     director.order()  
  50.     print builder2.GetRoom()  
注: 
因為 python 沒有 private 類型的成員,不過我們可以用命名為 __name 的變量代替,例如上例中的 __Room,為什麼這樣可以呢,我們用
  1. builder1=ConcreteBuilder1()   
  2. print dir(builder1)  
打印語句看出如下圖,

__name 實例化後變為 __ConcreteBuilder1__name ,就是避免外界對 __name 屬性的修改,從而達到了封閉性。更多請參考 "Private Variables and Class-local References"

5. Singleton 模式 
Singleton模式要求一個類有且僅有一個實例,並且提供了一個全局的訪問點,UML 如下: 
 

單例模式雖然不復雜,我一直認為這個模式是最簡單的,當我想用python實現的時候確犯難了,這篇文章也足足用了2星期才寫出來,期間各種查資料(省略1000個字),下面就來說說實現方法。先說以前比較熟悉的像C#這樣的語言,一般的實現方法是: 
1. 有一個私有的無參構造函數,這可以防止其他類實例化它。
2. 單例類被定義為sealed,目的是單例類也不被繼承,如果單例類允許繼承那麼每個子類都可以創建實例,這就違背了Singleton模式“唯一實例”的初衷,所以為了保險起見可以把該類定義成不允許派生,但沒有要求一定要這樣定義。
3. 一個靜態的變量用來保存單實例的引用。
4. 一個公有的靜態方法用來獲取單實例的引用,如果實例為 null 即創建一個。

上面是我熟悉的Singleton模式的創建方法,但是對於 python,既沒有 static 類型,也沒有私有方法和 sealed 修飾的類,如何實現呢?關於私有方法和屬性,我前面已經提到可以用 __name 形式為名稱定義方法名和屬性名來解決; 接著來幾種實作方式: 
- 利用 isinstance() 或 issubclass() 
isinstance(object, classinfo) 如果 object 是 classinfo 的一個實例或是子類,或者如果 classinfo 和 object 的類型是對象,或是該類型的對象的子類,返回 true; issubclass(class, classinfo) 如果class 是 classinfo 的一個子類返回 true。下面是利用 isinstance 實現的 Singleton 模式範例: 
- Singleton_test.py 
  1. #!/usr/bin/env python  
  2. class Singleton:  
  3.     __singleton = None  
  4.     @classmethod  
  5.     def getSingleton(cls):  
  6.         if not isinstance(cls.__singleton,cls):  
  7.             cls.__singleton = cls()  
  8.         return cls.__singleton  
  9.   
  10. class Test(Singleton) :  
  11.     def test(self):  
  12.         print self.__class__,id(self)  
  13.   
  14. class Test1(Test):  
  15.     def test1(self):  
  16.         print self.__class__,id(self), 'Test1'  
  17.   
  18. class Test2(Singleton):  
  19.     def test2(self):  
  20.         print self.__class__,id(self), 'Test2'  
  21.   
  22. if __name__== '__main__' :  
  23.     t1 = Test.getSingleton()  
  24.     t2 = Test.getSingleton()  
  25.   
  26.     t1.test()  
  27.     t2.test()  
  28.     assert(isinstance(t1,Test))  
  29.     assert(isinstance(t2,Test))  
  30.     assert(id(t1)== id(t2) )  
  31.   
  32.     t1 = Test1.getSingleton()  
  33.     t2 = Test1.getSingleton()  
  34.     assert(isinstance(t1,Test1))  
  35.     assert(isinstance(t2,Test1))  
  36.     assert(id(t1)== id( t2))  
  37.     t1.test()  
  38.     t1.test1()  
  39.     t2.test()  
  40.     t2.test1()  
  41.   
  42.     t1 = Test2.getSingleton()  
  43.     t2 = Test2.getSingleton()  
  44.     assert(isinstance (t1,Test2))  
  45.     assert(isinstance(t2,Test2))  
  46.     assert(id(t1)== id(t2))  
  47.     t1.test2()  
  48.     t2.test2()  
上面代碼的執行結果: 
 

從運行結果可以看出,我們可以控制同一個子類的生成同一個對象實例,但是如果Singleton類被繼承(不論是子類之間還是,子類的子類)不能控制生成一個實例。這個問題後面再探討。 

- 利用 __new__ 
提到 __new__ 就不能不說 __init__,先說說關於 __new__ 和 __init__ 的不同與用法: 
* object.__new__(cls[, ...]):調用創建 cls 類的一個新的實例。是靜態方法不用聲明。返回一個新對象的實例
* object.__init__(self[, ...]):當實例創建的時候調用。沒有返回值。
__new__ 在 __init__ 這個之前被調用:如果__new__返回一個cls的實例,那麼新的實例的__init__方法就會被調用,且self是這個新的實例。如果是自定義重寫__new__,沒有調用__init__的話__init__就不起作用了; 如果__new__不返回一個cls的實例,那麼新的實例的__init__方法就不會被調用。

示例代碼如下: 
  1. #!/usr/bin/env python  
  2. class Singleton( object ):  
  3.     def __new__(cls):  
  4.         print("Singleton __new__ being called")  
  5.         if not hasattr(cls, '_instance' ):  
  6.             cls._instance = object.__new__(cls)  
  7.         return cls._instance  
  8.     def __init__(self):  
  9.         print("Singletop __init__ being called")  
  10.   
  11. class MyClass1(Singleton):  
  12.     a = 1  
  13.     def __init__(self):  
  14.         print("MyClass1 __init__ being called")  
  15.   
  16. one = MyClass1()  
  17. two = MyClass1()  
  18.   
  19. two.a = 3  
  20. print 'one.a=' ,one.a  
  21.   
  22. assert(isinstance(one,MyClass1))  
  23. assert(isinstance(two,MyClass1))  
  24. print one.__class__,id(one)  
  25. print two.__class__,id(two)  
  26. print one == two  
  27. print one is two  
  28.   
  29. class MyClass2(Singleton):  
  30.     a = 2  
  31.     def __init__(self):  
  32.         print("MyClass2 __init__ being called")  
  33.   
  34. three = MyClass2()  
  35. three.a = 4  
  36. print 'three.a=' ,three.a  
  37. assert(isinstance(three,MyClass2))  
  38. print three.__class__,id(three)  
如上代碼,我們重寫了__new__ 方法,這段代碼返回的結果與第一種方法類似如下,也沒有解決多繼承多對象的問題。 
 

- 利用元類 __metaclass__ 
利用元類編寫單例其實原理和重寫 __new__ 是一樣的,都是在對象創建的時候進行攔截。範例代碼如下: 
  1. #!/usr/bin/env python  
  2. class Singleton(type) :  
  3.     def __init__(cls, name, bases, dict):  
  4.         print("Singleton __init__ being called")  
  5.         super(Singleton, cls).__init__(name, bases, dict)  
  6.         cls._instance = None  
  7.   
  8.     def __call__(cls):  
  9.         print("Singleton __call__ being called")  
  10.         if cls._instance is None:  
  11.             cls._instance = super(Singleton, cls).__call__()  
  12.         return cls._instance  
  13.   
  14. class MyClass1(object):  
  15.     __metaclass__ = Singleton  
  16.     a = 1  
  17.   
  18.     def __init__(self):  
  19.         print("MyClass1 __init__ being called")  
  20.   
  21. one = MyClass1()  
  22. two = MyClass1()  
  23.   
  24. two.a = 3  
  25. print 'one.a=' ,one.a  
  26.   
  27. assert(isinstance(one,MyClass1))  
  28. assert(isinstance(two,MyClass1))  
  29. print one.__class__,id(one)  
  30. print two.__class__,id(two)  
  31. print one == two  
  32. print one is two  
  33.   
  34. class MyClass2(object):  
  35.     __metaclass__ = Singleton  
  36.     a = 2  
  37.     def __init__(self):  
  38.         print("MyClass2 __init__ being called")  
  39.   
  40. three = MyClass2()  
  41. three.a = 4  
  42. print 'three.a=' ,three.a  
  43. assert(isinstance(three,MyClass2))  
  44. print three.__class__,id(three)  
Singleton 類在聲明是繼承了 type,對於 type 她其實是Python在背後用來創建所有類的元類。 
  1. class MyClass1(object):   
  2.       __metaclass__ = Singleton  
在聲明 MyClass1 時用到了以上的方式,原理是這樣的,MyClass1中有 __metaclass__ 這個屬性嗎?如果有,Python 會在內存中通過 __metaclass__ 創建一個名字為 MyClass1 的類對象。如果 Python 沒有找到 __metaclass__,它會繼續在object(父類)中尋找 __metaclass__ 屬性,並嘗試做和前面同樣的操作。如果Python 在任何父類中都找不到 __metaclass__,它就會在模塊層次中去尋找 __metaclass__,並嘗試做同樣的操作。如果還是找不到 __metaclass__ , Python 就會用內置的 type 來創建這個類對象。這里當程序發現 MyClass1 中有 __metaclass__,所以用 Singleton 類代替元類 type 創建這個類。 

其中還用到了 __call__object.__call__(self[, args...]) 當把一個實例當作方法來調用的時候,形如 instance(arg1,args2,...),那麼實際上調用的就是 instance.__call__(arg1 ,arg2,...),實際上 __call__ 模擬了 () 調用,作用在實例上,因此 __init__ 作用完了,才調用 __call__. 關於元類的具體解析請參考 這裡

- 利用 python Decorator Library——Singleton 
python 提供了豐富的裝飾者庫,其中就有現成的Singleton,官方參考鏈接參考 這裡. 這邊改寫了一個較簡單版本: 
  1. #!/usr/bin/env python  
  2. def singleton(cls):  
  3.     ''' Use class as singleton. '''  
  4.     def singleton_new():  
  5.         it = cls.__dict__.get ('__it__')  
  6.         if it is not None:  
  7.             return it  
  8.   
  9.         cls.__it__= cls()  
  10.         return cls.__it__  
  11.   
  12.     return singleton_new  
  13.   
  14.   
  15. @singleton  
  16. class Foo:  
  17.     a = 1  
  18.   
  19. one = Foo()  
  20. two = Foo()  
  21. two.a = 3  
  22. print 'one.a= ' ,one.a  
  23.   
  24. print one.__class__,id(one)  
  25. print two.__class__,id(two)  
  26. print one == two  
  27. print one is two  
  28.   
  29. @singleton  
  30. class Foo2:  
  31.     a = 1  
  32.   
  33. three = Foo2()  
  34. three.a= 4  
  35. print 'three.a= ' ,three.a  
  36. print three.__class__,id(three)  
總結:利用上面多種方法實現後,能實現對於一個類只有一個對象,但是不能避免的事類有繼承,有多個子類就可以生成多個子類的對象。其實在 python 中要實現單例模式並不需要藉用類的概念(java和C#需要類是因為所有代碼需要寫在類中),而是可以藉助模塊來實現,python 的模塊本身就是唯一的單例的,其中屬性和方法直接寫為全局的變量和方法即可。 

6. Prototype 模式  
原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。原型模式與工廠模式一樣都生成一個對象,區別就是工廠模式是創建新的對象,而原型模式是克隆一個已經存在的對象,所以在對像初始化操作比較複雜的情況下,很實用,它能大大降低耗時,提高性能,因為“不用重新初始化對象,而是動態地獲得對象運行時的狀態”。 

先來看看,原型模式的UML: 
客戶(Client)角色:客戶類提出創建對象的請求,讓一個原型克隆自身從而創建一個新的對象。
抽象原型(Prototype)角色:此角色給出所有的具體原型類所需的接口。
具體原型(Concrete Prototype)角色:被複製的對象。此角色需要實現抽象原型角色所要求的接口.

對於 python 實現原型模式有現成的 copy 模塊 可用。 
* copy.copy(x): Return a shallow copy of x.
* copy.deepcopy(x): Return a deep copy of x.

淺拷貝和深拷貝之間的區別僅適用於復合對象(包含其他對像也就是子對象,如 list 類或實例對象): 
* Shallow copy ——構建一個新的對象然後插入到原來的引用上。只拷貝父對象,不會拷貝對象的內部的子對象。
* Deep copy ——構造一個新的對像以遞歸的形式,然後插入複製到它原來的對像上。拷貝對象及其子對象

下面為範例代碼: 
  1. #!/usr/bin/env python  
  2. import copy  
  3.   
  4.   
  5. class ICloneable:  
  6.     def shallowClone(self, name):  
  7.         obj = copy.copy(self)  
  8.         print("Test of shallowcopy: %s/%s" % (id(self), id(obj)))  
  9.         obj.name = name  
  10.         return obj  
  11.   
  12.     def deepClone(self, name):  
  13.         obj = Resume(name)  
  14.         obj.sex = self.sex  
  15.         obj.age = self.age  
  16.         obj.work.companys = []  
  17.         for company in self.work.companys:  
  18.             obj.work.companys.append(copy.deepcopy(company))  
  19.         print("Test of deepcopy: %s/%s" % (id(self), id(obj)))  
  20.         obj.name = name  
  21.         return obj  
  22.   
  23. class Company:  
  24.     workData = ""  
  25.     name = ""  
  26.     phone = ""  
  27.   
  28.     def __init__(self, workData, name, phone):  
  29.         print("Company __init__ being called")  
  30.         self.name = name  
  31.         self.phone = phone  
  32.         self.workData = workData  
  33.   
  34. class WorkExperience:  
  35.     companys = []  
  36.   
  37.     def __init__(self):  
  38.         print("WorkExperience __init__ being called")  
  39.   
  40. class Resume(ICloneable):  
  41.     name = ""  
  42.     sex = ""  
  43.     age = 0  
  44.     work = None  
  45.   
  46.     def __init__(self, name):  
  47.         print("Resume __init__ being called")  
  48.         self.name = name  
  49.         self.work = WorkExperience()  
  50.   
  51.     def setPersonInfo(self, sex, age):  
  52.         self.sex = sex  
  53.         self.age = age  
  54.   
  55.     def setWorkExperience(self, workData, company_name, company_phone):  
  56.         company = Company(workData, company_name, company_phone)  
  57.         self.work.companys.append(company)  
  58.   
  59.     def addCompany(self, name, phone):  
  60.         company = Company(name, phone)  
  61.         self.work.companys.append(company)  
  62.   
  63.     def display(self):  
  64.         print('%s, %s, %d|%s' % (self.name,self.sex,self.age, id(self)))  
  65.         for c in self.work.companys:  
  66.             print("%s:" % (c.workData))  
  67.             print("\t%s (%s)|%s" % (c.name, c.phone, id(c)))  
  68.         print('')  
  69.   
  70. def client():  
  71.     a = Resume('Tom')  
  72.     a.setPersonInfo( 'm' , 29 )  
  73.     a.setWorkExperience( "1998-2000" , "ABC.COM""09123" )  
  74.   
  75.     b = a.shallowClone('Mary')  
  76.     b.setPersonInfo( 'f'18)  
  77.     b.setWorkExperience("2000-2006" , "QQ.COM""098" )  
  78.   
  79.     c = a.deepClone('John')  
  80.     c.setPersonInfo('m'36)  
  81.     c.setWorkExperience("2006-2009" , "360.COM""765" )  
  82.   
  83.     a.display()  
  84.     b.display()  
  85.     c.display()  
  86.     return  
  87.   
  88. if __name__ == '__main__' :  
  89.     client()  
執行結果: 
 

從結果可以看出,當 b 是 的淺拷貝,那麼 b 中的實例對象 WorkExperience 只會復制了 a 中的引用,當不論是 ab 哪一個修改都會改變 a 和 b 的 WorkExperience 實例; c 是 a 的深拷貝,創建了新的 WorkExperience 實例,所以 c 只會改變自己的WorkExperience


Supplement 
[OO 設計模式] Gossip@DesignPattern : Creational - Prototype Pattern (原型模式)

[Git 文章收集] Differences between git merge and git rebase

Source From  Here Preface Merging and rebasing are the two most popular way to applying changes from one branch into another one. They bot...