python 深入理解賦值 拷貝 引用 作用域

2021-09-25 06:41:24 字數 3313 閱讀 1382

**:

對於不可變物件和可變物件來說,淺複製都是複製的引用,只是因為複製不變物件和複製不變物件的引用是等效的(因為物件不可變,當改變時會新建物件重新賦值)。所以看起來淺複製只複製不可變物件(整數,實數,字串等),對於可變物件,淺複製其實是建立了乙個對於該物件的引用,也就是說只是給同乙個物件貼上了另乙個標籤而已。

舉個栗子:

在解釋之前應注意到:

標籤a所引用的物件的第一和第三個元素都是整數(不可變型別),而第二個物件是列表(可變型別),因此使用b=[:]進行淺複製的過程中會出現a和b引用的物件的第二個元素指向同乙個列表[1,2]的情況。正確的複製巢狀元素的方法是進行「深複製」(deep copy),方法是

+=操作首先呼叫物件的_iadd_方法,如果沒有該方法就呼叫_add_方法。需要注意的是:不可變物件沒有_iadd_方法。

l = [1, 2]

m = l

l = l + [3, 4]

print l, m

print "-------------------"

l = [1, 2]

m = l

l += [3, 4]

print l, m

以上**的前半部分中,用圖表示為:

而_iadd_屬於 in-place 操作,即原地操作,並不使用新的記憶體。用圖表示為:

在函式引數傳遞的時候,python其實就是把引數裡傳入的變數對應的物件的引用依次賦值給對應的函式內部變數。參照上面的例子來說明更容易理解,func_int中的區域性變數"a"其實是全部變數"t"所指向物件的另乙個引用,由於整數物件是不可變的,所以當func_int對變數"a"進行修改的時候,實際上是將區域性變數"a"指向到了整數物件"1"。所以很明顯,func_list修改的是乙個可變的物件,區域性變數"a"和全域性變數"t_list"指向的還是同乙個物件。

s = 'foo'

d = 

def f():

s = 'bar'

d['b'] = 2

f()print s  # foo

print d  # 

這是因為,在s = 'bar'這句中,它是「有歧義的「,因為它既可以是表示引用全域性變數s,也可以是建立乙個新的區域性變數,所以在python中,預設它的行為是建立區域性變數,除非顯式宣告global,global定義的本地變數會變成其對應全域性變數的乙個別名,即是同乙個變數。

在d['b']=2這句中,它是「明確的」,因為如果把d當作是區域性變數的話,它會報keyerror,所以它只能是引用全域性的d,故不需要多此一舉顯式宣告global。

上面這兩句賦值語句其實是不同的行為,乙個是rebinding(不可變物件), 乙個是mutation(可變物件).

但是如果是下面這樣:

d = 

def f():

d = {}

d['b'] = 2

f()print d  # 

在d = {}這句,它是」有歧義的「了,所以它是建立了區域性變數d,而不是引用全域性變數d,所以d['b']=2也是操作的區域性變數。

推而遠之,這一切現象的本質就是」它是否是明確的「。

接上面 5.3 的理論,下面咱們再看一例常見的錯誤:

# coding=utf-8

# 測試utf-8編碼

import sys

reload(sys)

sys.setdefaultencoding('utf-8')

list_a = 

def a():

list_a = [1]      ## 語句1

a()print list_a    # 

print "********************=="

list_b = 

def b():

b()print list_b    # [1]

大家可以看到為什麼 語句1 不能改變 list_a 的值,而 語句2 卻可以?他們的差別在哪呢?

陷阱:使用可變的預設引數

我多次見到過如下的**:

def foo(a, b, c=):

# do some more stuff

永遠不要使用可變的預設引數,可以使用如下的**代替:

def foo(a, b, c=none):

if c is none:

c = 

# do some more stuff

‍‍與其解釋這個問題是什麼,不如展示下使用可變預設引數的影響:‍‍

in[2]: def foo(a, b, c=):

...        print(c)

...in[3]: foo(1, 1)

[1, 1]

in[4]: foo(1, 1)

[1, 1, 1, 1]

in[5]: foo(1, 1)

[1, 1, 1, 1, 1, 1]

同乙個變數c在函式呼叫的每一次都被反覆引用。這可能有一些意想不到的後果。

深入理解Python變數與賦值

python是一門很特殊的語言,它和c語言不一樣,python是 弱型別 的指令碼語言,變數在宣告的時候並不定義它的型別,而是通過賦值的型別體現出來,所以一般python變數在宣告時需要賦初值.在c語言中,給變數賦值時,需要先指定資料型別,同時會開闢一塊記憶體區域,用於儲存值,例如 int a 0 ...

深入理解PHP物件賦值

1 2 深入理解php物件賦值 3echo 45 obj new stdclass 6 obj name pig 7var dump obj object stdclass 1 1 89 copy obj obj copy都是new stdclass返回的同乙個識別符號的拷貝 10var dump ...

深入理解深拷貝與淺拷貝

深拷貝和淺拷貝是經常在面試中會出現的,主要考察你對基本型別和引用型別的理解深度。我在無數次的面試中,應聘者還沒有乙個人能把這個問題回答情況,包括很多機構的培訓老師。這篇文章會讓你把基本型別和引用型別的區別搞得清清楚楚,搞清楚這兩者的區別,你對任何程式語言的都不怕,因為,這不是js一門語言,是任何程式...