天天看點

Python比較操作符、變量指派、對象拷貝

Python比較操作符、變量指派、對象拷貝

Python比較操作符、變量指派、對象拷貝

目錄

  • Python比較操作符、變量指派、對象拷貝
    • 1. 比較操作符 == 和 is
      • 1.1 差別
      • 1.2 執行個體
    • 2. 變量及其指派
      • 2.1 概念和邏輯關系
      • 2.2 Python函數的參數傳遞
      • 2.3 思考題
    • 3. 淺拷貝和深度拷貝
      • 3.1 淺拷貝概念
      • 3.2 淺拷貝方法——可變對象
      • 3.3 深度拷貝概念
      • 3.4 深度拷貝方法
      • 3.5 執行個體
        • 3.5.1 淺拷貝和指派的差別——是否會建立一個新對象
        • 3.5.2 字元串、數字不能實作拷貝
        • 3.5.3 元組的淺拷貝和深度拷貝
        • 3.5.4 淺拷貝和深度拷貝的影響
        • 3.5.6 用一個深度拷貝,拷貝一個無限嵌套的清單,是否相等
    • 總結

1. 比較操作符 == 和 is

1.1 差別
  • == 操作符比較對象之間的值是否相等
  • is 操作符比較的是對象的身份辨別是否相等,即是否是同一個對象,是否指向同一個記憶體位址
  • is 操作符的速度效率通常要優于==,因為is操作符不能被重載,執行is操作隻是簡單的擷取對象的ID,并進行比較,而等于操作符則會遞歸地周遊對象所有值,并逐一比較
  • 當比較一個變量與一個單例時,通常使用is
1.2 執行個體
# 比較兩個對象
def compare(A, B):
    if A == B:
        print(f"{A} == {B}:{True}")
    else:
        print(f"{A} == {B}:{False}")
    if A is B:
        print(f"{A} is {B}:{True}")
    else:
        print(f"{A} is {B}:{False}")
        print(id(A), id(B))
           
A = -7
B = -7
compare(A, B)
C = 4
D = 4
compare(C, D)
           
"""
python内部對**-5到256的整型**維持一個數組,起到一個緩存的作用,使得性能優化,是以,在-5到256之間的整型數字比較,都相等。上述代碼是在jupyter notebook中運作的,如果在pycharm中運作,則都是True,pycharm中做了優化。
"""
-7 == -7:True
-7 is -7:False
139667038587504 139667038587152
4 == 4:True
4 is 4:True
           
# 比較一個變量
if a is None:
      ...

if a is not None:
      ...
           

2. 變量及其指派

2.1 概念和邏輯關系
  • 變量的指派,隻是表示讓變量指向了某個對象,并不表示拷貝對象給變量;而一個對象,可以被多個變量所指向。
  • 可變對象(清單,字典,集合等等)的改變,會影響所有指向該對象的變量。
  • 對于不可變對象(字元串、整型、元組等等),所有指向該對象的變量的值總是一樣的,也不會改變。但是通過某些操作(+= 等等)更新不可變對象的值時,會傳回一個新的對象。
  • 變量可以被删除,但是對象無法被删除,需要通過python垃圾回收機制回收。
2.2 Python函數的參數傳遞

​ 是指派傳遞,python裡所有的資料類型都是對象,是以參數傳遞時,隻是讓讓新變量與原變量指向相同的對象而已,并不存在值傳遞或是引用傳遞(c++等語言中)一說。

2.3 思考題

2.3.1 變量指向的是同一個對象嗎?——查id

# l1,l2,分别配置設定了記憶體空間,不是指向同一個對象
# l2,l3,是指向
l1 = [1, 2, 3] # 建立了新對象
l2 = [1, 2, 3] # 建立了新對象
l3 = l2  # 指向同一個對象
print(id(l1), id(l2), id(l3))
# 1479611736648 1479611736712 1479611736712
           

2.3.2 變量被修改了嗎?

# 字典是可變對象,對象改變,會影響所有指向該對象的變量
def func(d):
    d['a'] = 10
    d['b'] = 20
d = {'a': 1, 'b': 2}
func(d)
print(d) # {'a': 10, 'b': 20}
           

3. 淺拷貝和深度拷貝

3.1 淺拷貝概念

淺拷貝是指重新配置設定一塊記憶體,建立一個新的對象,裡面的元素是原對象内第一層對象的引用,是以,如果原對象中的元素是可變的,改變其也會影響拷貝後的對象,存在一定的副作用。

3.2 淺拷貝方法——可變對象
  • 類型工廠函數:
    • 是指不通過類而是通過函數來建立對象,list(),set(),int(),dict(),tuple(),str()等資料類型本身的構造器
    • 淺拷貝中适用的是可變對象,是以,list(),set(),dict()适用,其餘不适用
  • 切片操作:清單
  • copy子產品中copy方法:copy.copy()
3.3 深度拷貝概念

深度拷貝是指重新配置設定一塊記憶體,建立一個新的對象,并且将元對象中的元素,以遞歸的方式,通過建立新的子對象拷貝到新對象中,是以,新對象和原對象沒有任何關聯。另外,深度拷貝中會維護一個字典,記錄已經拷貝的對象及其ID,來提高效率并放置無限遞歸的發生。

3.4 深度拷貝方法
  • copy子產品中的deepcopy方法:copy.deepcopy()
3.5 執行個體

3.5.1 淺拷貝和指派的差別——是否會建立一個新對象

l1 = [1, 2, 3]
l2 = list(l1) # 使用工廠函數實作淺拷貝
compare(l1, l2)
a = [1, 2, 3]
b = a  # 變量指派
compare(a, b)
           
[1, 2, 3] == [1, 2, 3]:True
[1, 2, 3] is [1, 2, 3]:False
140603550652928 140603550660720
[1, 2, 3] == [1, 2, 3]:True
[1, 2, 3] is [1, 2, 3]:True
           

3.5.2 字元串、數字不能實作拷貝

import copy
t1 = 4
t2 = copy.deepcopy(t1) # 對一個元組實作深度拷貝
compare(t1, t2)
sr1 = "adc"
sr2 = str(sr1) # 字元串不能建立淺拷貝
compare(sr1, sr2)
           
4 == 4:True
4 is 4:True
adc == adc:True
adc is adc:True
           

3.5.3 元組的淺拷貝和深度拷貝

import copy
s1 = (1, 2, 3,[1,2])
s2 = copy.copy(s1) # 元組不能實作淺拷貝
compare(s1, s2)python
           
(1, 2, 3, [1, 2]) == (1, 2, 3, [1, 2]):True
(1, 2, 3, [1, 2]) is (1, 2, 3, [1, 2]):True
           
import copy
t1 = (1, 2, 3)
t2 = copy.deepcopy(t1) # 隻包含不可變對象的元組不能實作深拷貝
compare(t1, t2)
           
(1, 2, 3) == (1, 2, 3):True
(1, 2, 3) is (1, 2, 3):True
           
import copy
s1 = (1, 2, 3,[1,2])
s2 = copy.deepcopy(s1) # 對一個包含可變對象的元組可以實作深度拷貝
compare(s1, s2)
           
(1, 2, 3, [1, 2]) == (1, 2, 3, [1, 2]):True
(1, 2, 3, [1, 2]) is (1, 2, 3, [1, 2]):False
139823086708208 139823086707728
           

3.5.4 淺拷貝和深度拷貝的影響

# 淺拷貝:原對象的改變可能會影響新對象
import copy
l1 = [[1, 2], (30, 40)]
print(f"原對象:{l1}")
l2 = copy.copy(l1)
l1.append(100)
l1[0].append(3)
print(f"原對象修改:{l1}")
print(f"淺拷貝後的新對象:{l2}")
           
原對象:[[1, 2], (30, 40)]
原對象修改:[[1, 2, 3], (30, 40), 100]
淺拷貝後的新對象:[[1, 2, 3], (30, 40)]
           
# 深度拷貝:新對象和原對象沒有任何關聯
import copy
l1 = [[1, 2], (30, 40)]
print(f"原對象:{l1}")
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
print(f"原對象修改:{l1}")
print(f"深度拷貝後的新對象:{l2}")
           
原對象:[[1, 2], (30, 40)]
原對象修改:[[1, 2, 3], (30, 40), 100]
深度拷貝後的新對象:[[1, 2], (30, 40)]
           

3.5.6 用一個深度拷貝,拷貝一個無限嵌套的清單,是否相等

import copy
x = [1]
x.append(x)
y = copy.deepcopy(x)
print(len(y)) # 輸出為2
if x == y:
    print(True)
else:
    print(False)
# 運作報錯:RecursionError: maximum recursion depth exceeded in comparison
           

總結

一、指派:

在 Python 中,對象的指派就是簡單的對象引用,這點和 C++不同

二、淺拷貝(shallow copy):

淺拷貝會建立新對象,重新配置設定記憶體,其内容非原對象本身的引用,而是原對象内第一層對象的引用。淺拷貝有三種形式:切片操作、工廠函數、copy 子產品中的 copy 函數。

三、深拷貝(deep copy):

深拷貝隻有一種形式,copy 子產品中的 deepcopy()函數。深拷貝和淺拷貝對應,深拷貝拷貝了對象的所有元素,包括多層嵌套的元素。是以,它的時間和空間開銷要高。

四、拷貝的注意點:

1、對于非容器類型,如數字、字元,以及其他的“原子”類型,沒有拷貝一說,産生的都是原對象的引用。

2、如果元組變量值包含原子類型對象,即使采用了深拷貝,也隻能得到淺拷貝。

寫在最後:

吐血整理,不過好在是終于理清了,文章中所有的例子可以在jupyter nootbook上運作,轉載請注明出處,謝謝!!

繼續閱讀