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 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...