天天看點

python可變不可變資料類型錯誤

今天突來興趣,留檔一些python常見的錯誤和思維

一圖讀懂:(2019元旦補:)

python可變不可變資料類型錯誤

下面内容感興趣可以看,不然就隻看一圖讀懂:

在說錯誤之前,先來看一下在python中的哪些是可變資料和哪些是不可變資料吧
1.可變資料類型
清單list
字典dict
2.不可變資料類型
整型int
浮點型float
字元串型string
元組tuple。

下面我們以int和list為例,來說一下“可變資料類型”和“不可變資料類型”的差別

(1).不可變資料類型分析:
x=1
y=1
print(id(x))
print(id(y))
           

結果

1376539424

1376539424

    可以看出,x和y綁定同一個位址(類比了解 用c的指針了解,p1和p2中的,指針變量p1和p2的值是一樣,都是儲存了同一個記憶體的位址,*p1意思就是":p1的内容(值)所指(代表)的位址的内容"),就是x和y其實是引用了同一個對象,即1,也就是說記憶體中對于1隻占用了一個位址,而不管有多少個引用指向了它,都隻有一個位址值,隻是有一個引用計數器會記錄指向這個位址的引用到底有幾個而已。

(2).可變資料類型分析:
x=[2,3]
print(id(x))
x=[2,3]
print(id(x))
y=[2,3]
print(id(y))
           

結果

2285423966792

2285423964360

2285423964388

    兩次x變量綁定在不同的位址(即變量x(引用x)的值不同,有人說:python中 [變量]更準确叫法是[名字],指派操作 = 就是把一個名字綁定到一個對象上。或者 類比了解(python中的變量是沒有類型資訊和不占記憶體區域的),c中一個指針變量(java php js中叫引用)指向了一個對象(記憶體空間)),也就是說其實建立了兩個不同的對象,這一點明顯不同于不可變資料類型,是以對于可變資料類型來說,具有同樣值的對象是不同的對象,即在記憶體中儲存了多個同樣值的對象,位址是不同。

重點:

x=[2,3]
print(id(x))
x.append(4)
print(x)
x += [1]
print(x)
print(id(x))
           

結果

2655391136328

[2, 3, 4]

[2, 3, 4, 1]

2655391136328

    我們對清單進行添加操作,分别x.append(4)和x += [1],發現這兩個操作使得x引用的對象值變成了上面的最終結果,但是x變量綁定的位址依舊是2655391136328,也就是說對x引用指向的對象進行的操作,不會改變x變量的值(x引用指向的位址),隻是在位址後面又擴充了新的位址,改變了位址裡面存放的值,是以可變資料類型的意思就是說對一個變量進行操作時,其值是可變的,值的變化并不會引起建立對象,即位址是不會變的,隻是位址中的内容變化了或者位址得到了擴充。

參考:http://blog.csdn.net/dan15188387481/article/details/49864613

開始常見錯誤

1.用一個可變的值作為預設值(可變資料類型作為函數定義中的預設參數)
def fn(var1,var2=[]):
	var2.append(var1)
	print(var2)
	print(id(var2))
fn(1)
fn(2)
fn(3)
           

輸出結果:

[1]

2275344529992

[1, 2]

2275344529992

[1, 2, 3]

2275344529992

    在Python裡,函數的預設值是在函數定義的時候執行個體化的,而不是在調用的時候。如同上面,當解析器運作到定義函數時,清單[]被執行個體化為函數定義的一部分。當函數運作時,它并不是每次都被執行個體化。這意味着,這個函數會一直使用完全一樣的清單對象,除非我們提供一個新的對象傳進去。

就是說 函數定義時,如果沒有傳進新對象,可變類型[]的位址一直沒有變化,var2變量一直綁定在同一個位址上,這樣就回到了上面可變資料類型分析問題上了

代碼修正:

def fn(var1,var2=None):
	if not var2:
	#if var2 is None:
		var2=[]
	var2.append(var1)
	print(var2)
fn(1)
fn(2)
           

結果

[1]

[2]

類似例子

import time
def print_now(now=time.time()):
    print(now)
print_now()
print_now()
print_now()
           

結果

1513001005.1218028

1513001005.1218028

1513001005.1218028

    跟上面一樣,time.time() 的值是可變的 類似可變資料類型,那麼它隻會在函數定義的時候計算,是以無論調用多少次,都會傳回相同的時間 — 這裡輸出的時間是程式被Python解釋運作的時間。

2.可變資料類型作為類變量
class Url(object):
	urls = []
	def add_url(self,url):
		self.urls.append(url)
		print(id(self.urls))
a = Url()
a.add_url('www.baicai.com')
b = Url()
b.add_url('www.wljsb.com')
print(a.urls)
print(b.urls)
           

結果

2264790084168

2264790084168

[‘www.baicai.com’, ‘www.wljsb.com’]

[‘www.baicai.com’, ‘www.wljsb.com’]

    這兩個對象怎麼會都有這兩個元素呢?

    這和第一個問題是類似的。建立類定義時,urls清單将被執行個體化。該類所有的執行個體使用相同的清單。在有些時候這種情況是有用的,但大多數時候你并不想這樣做。

就是說,清單的位址一直沒有變化,urls變量綁定在同一個位址上,最終還是回到上面的分析中

你希望每個對象有一個單獨的儲存。為此,我們修改代碼為:

class Url(object):
	def __init__(self):
		self.urls = []
	def add_url(self,url):
		self.urls.append(url)
		print(id(self.urls))
a = Url()
a.add_url('www.baicai.com')
b = Url()
b.add_url('www.wljsb.com')
print(a.urls)
print(b.urls)
           

結果

2019524248968

2019524233800

[‘www.baicai.com’]

[‘www.wljsb.com’]

3.了解python變量(也叫可變配置設定錯誤)

a = {'1': "one", '2': 'two'}
b = a
b['3'] = 'three'
print(a)
           

結果

{‘2’: ‘two’, ‘3’: ‘three’, ‘1’: ‘one’}

    現在看這個很簡單了, a 和 b 變量都是綁定在同一個對象上,就相當于看同一台電視,誰換台都受影響的。

結尾,根據這些也可以判斷python中函數的參數 為啥有些人老是搞糊塗 是平常說的 傳值還是傳引用(位址), 其實 python中參數的傳遞本質上是一種 指派操作
def foo(arg):
    arg = 2
    print(arg)
a = 1
foo(a) 
print(a) 
           

結果

2

1

上面根據不可變類型分析,a 和 arg 綁定的位址不同 類似傳值操作

為啥有人會搞糊塗,說參數是傳引用呢?看下面

def bar(args):
    args.append(2)
b = [1]
print(b)
print(id(b))
bar(b)
print(b)
print(id(b))
           

結果

[1]

3001474107976

[1, 2]

3001474107976

    看到這個結果,你思考一下,是不是很像把清單的位址傳了進去,這就是c java php js的思維了,因為他們覺得調用函數後 再輸出b清單是[1,2] ,函數内部修改了函數外部的變量,看位址輸出都沒有變化,是以認為是傳引用。

    這就是python變量和其他語言的不同之處了,上面根據可變類型分析,b和arg綁定在了同一個位址上,對同一個清單對象進行操作的。

python的變量和其他語言不同,可以說把傳值傳引用的說法統一成,說 指派操作(綁定),根據不可變類型和可變類型分析,就了解到具體的操作變化了

以上内容總結成:變量當作标簽貼在記憶體上,看是可變還是不可變,傳參是時候,可變的話在記憶體基礎上變,不可變的話,就開辟另外記憶體空間,把便簽貼過來.

python常注意點:

a =(1,) #元組隻有一個元素是後面要有逗号
b = a[::-1] #這是反向切片指派
a[-1:6] #分片中最左邊的索引比它右邊的晚出現在序列中,結果就是一個空序列