python原子操作
⑴ python 為什麼有深拷貝淺拷貝
在寫Python過程中,經常會遇到對象的拷貝,如果不理解淺拷貝和深拷貝的概念,你的代碼就可能出現一些問題。所以,在這里按個人的理解談談它們之間的區別。
一、賦值(assignment)
在《Python FAQ1》一文中,對賦值已經講的很清楚了,關鍵要理解變數與對象的關系。
12345
>>> a = [1, 2, 3]>>> b = a>>> print(id(a), id(b), sep='\n')
在Python中,用一個變數給另一個變數賦值,其實就是給當前內存中的對象增加一個「標簽」而已。
如上例,通過使用內置函數 id() ,可以看出 a 和 b 指向內存中同一個對象。a is b會返回 True 。
二、淺拷貝(shallow )
注意:淺拷貝和深拷貝的不同僅僅是對組合對象來說,所謂的組合對象就是包含了其它對象的對象,如列表,類實例。而對於數字、字元串以及其它「原子」類型,沒有拷貝一說,產生的都是原對象的引用。
所謂「淺拷貝」,是指創建一個新的對象,其內容是原對象中元素的引用。(拷貝組合對象,不拷貝子對象)
常見的淺拷貝有:切片操作、工廠函數、對象的()方法、模塊中的函數。
12345678910
>>> a = [1, 2, 3]>>> b = list(a)>>> print(id(a), id(b)) # a和b身份不同140601785066200 140601784764968>>> for x, y in zip(a, b): # 但它們包含的子對象身份相同... print(id(x), id(y))... 140601911441984 140601911442048
從上面可以明顯的看出來,a 淺拷貝得到 b,a 和 b 指向內存中不同的 list 對象,但它們的元素卻指向相同的 int 對象。這就是淺拷貝!
三、深拷貝(deep )
所謂「深拷貝」,是指創建一個新的對象,然後遞歸的拷貝原對象所包含的子對象。深拷貝出來的對象與原對象沒有任何關聯。
深拷貝只有一種方式:模塊中的deep函數。
1234567891011
>>> import >>> a = [1, 2, 3]>>> b = .deep(a)>>> print(id(a), id(b))140601785065840 140601785066200>>> for x, y in zip(a, b):... print(id(x), id(y))... 140601911441984 140601911442048
看了上面的例子,有人可能會疑惑:
為什麼使用了深拷貝,a和b中元素的id還是一樣呢?
答:這是因為對於不可變對象,當需要一個新的對象時,python可能會返回已經存在的某個類型和值都一致的對象的引用。而且這種機制並不會影響 a 和 b 的相互獨立性,因為當兩個元素指向同一個不可變對象時,對其中一個賦值不會影響另外一個。
我們可以用一個包含可變對象的列表來確切地展示「淺拷貝」與「深拷貝」的區別:
>>> import >>> a = [[1, 2],[5, 6], [8, 9]]>>> b = .(a) # 淺拷貝得到b>>> c = .deep(a) # 深拷貝得到c>>> print(id(a), id(b)) # a 和 b 不同139832578518984 139832578335520>>> for x, y in zip(a, b): # a 和 b 的子對象相同... print(id(x), id(y))... 139832578622816 139832578623104>>> print(id(a), id(c)) # a 和 c 不同139832578518984 139832578622456>>> for x, y in zip(a, c): # a 和 c 的子對象也不同... print(id(x), id(y))... 139832578622816 139832578623392
從這個例子中可以清晰地看出淺拷貝與深拷貝地區別。
總結:
1、賦值:簡單地拷貝對象的引用,兩個對象的id相同。
2、淺拷貝:創建一個新的組合對象,這個新對象與原對象共享內存中的子對象。
3、深拷貝:創建一個新的組合對象,同時遞歸地拷貝所有子對象,新的組合對象與原對象沒有任何關聯。雖然實際上會共享不可變的子對象,但不影響它們的相互獨立性。
淺拷貝和深拷貝的不同僅僅是對組合對象來說,所謂的組合對象就是包含了其它對象的對象,如列表,類實例。而對於數字、字元串以及其它「原子」類型,沒有拷貝一說,產生的都是原對象的引用。
⑵ Python多線程之threading之Lock對象
要介紹Python的 threading 模塊中的 Lock 對象前, 首先應該了解以下兩個概念:
1.基本概念 : 指某個函數/函數庫在多線程環境中被調用時, 能夠正確地處理多個線程之間的 共享變數 , 使程序功能正常完成. 多個線程訪問同一個對象時, 如果不用考慮這些線程在運行時環境下的調度和交替執行, 也不需要進行額外的同步, 或者在調用方進行任何其他操作,調用這個對象的行為都可以獲得正確的結果, 那麼這個對象就是線程安全的. 或者說: 一個類或者程序所提供的介面對於線程來說是 原子操作 或者多個線程之間的切換不會導致該介面的執行結果存在二義性, 也就是說我們不用考慮同步的問題.
2.示例 : 比如有間銀行只有1000元, 而兩個人同時提領1000元時,就有可能拿到總計2000元的金額. 為了避免這個問題, 該間銀行提款時應該使用 互斥鎖 , 即意味著對同一個資源處理時, 前一個提領交易完成後才處理下一筆交易.
3.線程安全意義 :
4.是否線程安全 :
5.資源競爭 : 即多個線程對同一個資源的改寫時, 存在的一種競爭. 如果僅僅是讀操作, 則不存在資源競爭的情況.
1.基本概念 : 因為存在上述所說的 線程安全與資源競爭 的情況, 所以引入了 線程鎖 . 即通過鎖來進行資源請求的限制, 以保證同步執行,避免資源被污染或預期結果不符. 線程鎖存在兩種狀態: 鎖定(locked)和非鎖定(unlocked).
2.基本方法 :
3.使用示例 :
上述示例如果在不加鎖的情況下, 將會出現列印順序混亂的情況, 不過最終結果都是正確的, 因為即使線程交替執行, 但最終的結果都是一致.