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

全局變量

在函數以外建立的變量屬於__main__,又被稱爲全局變量。它們能夠在__main__中的任意函數中訪問,與局部變量在函數結束時消失不一樣,全局變量能夠在不一樣函數的調用之間持久存在。全局變量經常用做標誌(Flags)。標誌是一種布爾型變量,能夠標誌一個條件是否爲真。html

verbose = True

def example():
    if verbose:
        print('你好,今每天氣很好!')
    else:
        print('你好')
複製代碼

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

verbose = True

def example():
    global verbose
    verbose = False
    print('你好')
複製代碼

對象、值和別名

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

先來看一段代碼:bash

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是相等的。相同一定相等,相等不必定相同。app

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和==的結果不一樣了。ui

But wait,那麼爲何當a、b的值爲1和'hello'時,is與==的結果是同樣的呢?這就要說到python的小整數池和垃圾回收機制了。python爲了讓運行速度快些,在內存中專門開闢了一塊區域放置-5到256,全部表明-5到256的對象都會指向這個區域。spa

相似的,字符串類型做爲Python中最經常使用的數據類型之一,Python解釋器爲了提升字符串使用的效率和使用性能,作了不少優化。

例如:Python解釋器中使用了intern(字符串駐留)的技術來提升字符串效率,什麼是intern機制?即值一樣的字符串對象僅僅會保存一份,放在一個字符串儲蓄池中,是共用的,固然,確定不能改變,這也決定了字符串必須是不可變對象。

同時,若是字符串中有空格,默認不啓用intern機制。對字符串儲蓄池中的字符串使用is和==比較會獲得相同的結果。:

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

a = 'hello' 
b = 'hello' 
id(a) # 1939745887600
id(b) # 1939745887600
複製代碼

注意:在shell中,僅有如下劃線、數字、字母組成的字符串會被intern。而pycharm中只要是同一個字符串不超過20個字符都被加入到池中


python傳參

python函數參數傳遞是引用傳遞:

def test(n):
    print(id(n))
k = "string"
id(k) # 2305161642256
test(k) # 2305161642256
複製代碼

深淺拷貝

經常使用的拷貝方式有:

  • 沒有限制條件的分片表達式(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],字符串是不可變對象,因此仍指向原地址,對於list
複製代碼

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

相關文章
相關標籤/搜索