Python中的賦值、引用和深淺拷貝

全局變量

在函數之外創建的變量屬於__main__,又被稱爲全局變量。它們可以在__main__中的任意函數中訪問,與局部變量在函數結束時消失不同,全局變量可以在不同函數的調用之間持久存在。

全局變量常常用作標誌(Flags)。標誌是一種布爾型變量,可以標誌一個條件是否爲真。

verbose = True

def example():
    if verbose:
        print('你好,今天天氣很好!')
    else:
        print('你好')

如果在函數裏嘗試給全局變量賦值,必須先用global關鍵字進行聲明,否則函數會創建一個同名局部變量而不是使用全局變量。

verbose = True

def example():
    global verbose
    verbose = False
    print('你好')

對象、值和別名

先來看一段代碼:

b = [1, 2, 3]
a = b
print(a is b) # True
b.append(4)
print(a) # [1, 2, 3, 4]

a is b返回True,說明這python內部a與b是相同的,變量a與變量b都指向同一個對象。此時稱a和b爲這個對象的別名。當對象的值發生改變時,a和b自然也會隨之改變。如果a、b只是值相等而不指向同一個對象,我們稱a與b是相等的。相同必定相等,相等不一定相同。

a = [1, 2, 3]
b = [1, 2, 3]
print(a is b) # False

我們平時說的【變量】其實只是標籤,是對內存中對象的引用。賦值操作只是給變量一個指向對象的引用

is與==的區別

寫代碼的時候常常用is和==來比較兩個對象是否相等,但是它們有什麼不同呢?參考下面的例子:

a = 1
b = 1
a == b # True
a is b # True

a = 888
b = 888
a == b # True
a is b # False

a = 'hello'
b = 'hello'
a is b # True
a == b # True

a = 'hello world'
b = 'hello world'
a == b # True
a is b # False

is和==的結果不同!不是說好的都是比較兩個對象是否相等嗎?怎麼到這裏變了樣了?不急,先介紹一下python內置的一個函數:id(),這個函數會打印參數的內存地址,讓我們來試試:

a = 888
b = 888
id(a) # 1939743592336
id(b) # 1939745557808

a = 'hello world'
b = 'hello world'
id(a) # 1939745897200
id(b) # 1939745912624

可以看到,儘管a、b的值是相同的,但是其內存地址卻不同。那麼答案就很顯然了,is比較的是兩個對象的內存地址是否相等,==比較的是兩個對象的值是否相等。這樣就能解釋爲什麼is和==的結果不同了。But wait,那麼爲什麼當a、b的值爲1和'hello'時,is與==的結果是一樣的呢?這就要說到python的小整數池和垃圾回收機制了。python爲了讓運行速度快些,在內存中專門開闢了一塊區域放置-5到256,所有代表-5到256的對象都會指向這個區域。類似的,python爲短文本也開闢了這樣的一塊內存空間。所以這時is和==會得到相同的結果:

a = 1
b = 1
id(a) # 1963327952
id(b) # 1963327952

a = 'hello' 
b = 'hello' 
id(a) # 1939745887600
id(b) # 1939745887600

可變對象與不可變對象

在Python中,string、tuple和number是不可變對象,而list、dict等是可變對象。

對於變量,python函數參數傳遞有時是引用傳遞,有時是值傳遞。如果這個變量對應的對象值可變,就認爲是引用,函數內外是同一個變量,如果這個變量對應的對象值不可改變,就認爲是賦值,函數內外是不同的變量:

# 不可變對象按值傳遞
n = 5
def test(n):
    n = 10
test(n)
print(n) # n 仍是5

# 可變對象按引用傳遞
n = [5]
def test(n):
    n[0] = 10
test(n)
print(n) # n 爲[10]

深淺拷貝

常用的拷貝方式有:

- 沒有限制條件的分片表達式(L[:])能夠複製序列,但此法只能淺層複製。
- 字典 copy 方法,D.copy() 能夠複製字典,但此法只能淺層複製
- 有些內置函數,例如 list,能夠淺拷貝 list(L)
- copy 標準庫模塊能夠生成完整拷貝:deepcopy 本質上是遞歸 copy,是深層複製

淺拷貝

淺拷貝屬於“新瓶裝舊酒”,即生成了一個新的變量,而變量所指向的對象和原來的是一樣的:

l = ["hello", [2, 3, 4]]
id(l) # 3048239386824
[id(i) for i in l] # [1524761040, 1524761072]

k = l.copy()
id(k) # 3048239387080,地址不同,k是另一個變量
[id(i) for i in k] # [1524761040, 1524761072],地址相同,指向同一個變量

深拷貝

深拷貝屬於“新瓶裝新酒”,即生成了一個新變量,指向和原對象相等的新對象(不可變對象除外):

import copy

l = ["hello world", [2, 3, 4]]
id(l) # 3048239386824
[id(i) for i in l] # [3048239385040, 3048239387080]

k = copy.deepcopy(l)
id(k) # 3048240927048,地址不同,k是另一個變量
[id(i) for i in k]  # [3048239385040, 3048240927304],字符串是不可變對象,所以仍指向原地址,對
python學習交流羣:125240963

轉載至:https://zhuanlan.zhihu.com/p/39128883

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章