欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python黑魔法之參數(shù)傳遞

 更新時間:2016年02月12日 18:22:34   作者:icedoom  
這篇文章主要介紹了python黑魔法之參數(shù)傳遞,分析了python參數(shù)傳遞的方法,感興趣的小伙伴們可以參考一下

我們都聽說,python世界里面,萬物皆對象。
怎么說萬物皆對象呢?最常見的:

> class A: pass
> a = A()

我們說a是一個對象。
那么既然是萬物了,其實A也是對象。3 也是對象。True 也是對象。"hello" 也是對象。
> def Func(): pass
o~yee, Func 也是對象。
那么對象之間的傳遞是如何呢?我們看看下面兩個簡單的例子:

> a = 3
> b = a
> b = 3 + 1
> print b
4
> print a
3

> a = []
> b = a
> b.append(1)

> print a
[1]
> print b
[1]

不是都說python所有對象都是引用傳遞嗎?為毛第一個b不是3?
好吧。事實是,在python的實現(xiàn)上,對象分為mutable 和 immutable。
這里說的對象分類,是說在實現(xiàn)上具備這樣的特性。而非對象本身的屬性。
什么是immutable?表示對象本身不可改變。這里先記住一點,是對象 本身 不可改變。
什么叫做對象本身不可改變呢?
一個簡單的例子:

> a = (1,2,3)
> a[0] = 10  

TypeError: 'tuple' object does not support item assignment
元組的元素在初始化后就不能再被改變。也就是說,元組對象具備immutable的特性。
那么很簡單,相對的,mutable 就是可變的。比如:

> a = {}
> a[0] = 10

有了上面的兩個例子,相信大家已經(jīng)有了基本的認識。
那么,在python世界中,哪些是具備immutable特性,哪些又是mutable的呢?
簡單講,基本類型都是immutable, 而object都是mutable的。
比如說:int, float, bool, tuple 都是immutable。
再比如:dict, set, list, classinstance 都是mutable的。
那么問題來了。既然說基本類型是 immutable ,那么最上面的 b = 3 + 1 為什么不會像tuple一樣,拋異常呢?
原因在于,int 對+操作會執(zhí)行自己的__add__方法。而__add__方法會返回一個新的對象。
事實是,當(dāng)基本類型被改變時,并不是改變其自身,而是創(chuàng)建了一個新的對象。最終返回的是新的對象的引用。
怎么證明?
我們可以使用一個叫做id()的函數(shù)。該函數(shù)會返回對象的一個唯一id(目前的實現(xiàn)可以間接理解為對象的內(nèi)存地址)。
那么我們看下:

> a = 3
> id(a)
140248135804168

> id(3)
140248135804168

> id(4)
140248135804144

> a = a + 1
> id(a)
140248135804144

you see ? 當(dāng)我們執(zhí)行a=a+1 后,id(a) 已經(jīng)改變了。
深究一點,為什么會這樣呢?
其實,a = a + 1 經(jīng)歷了兩個過程:

  • 1、a + 1
  • 2、a 賦值

第2步只是一個引用的改變。重點在第1步。a + 1,那么python實際上會調(diào)用a.__add__(1)。
對于int類型__add__函數(shù)的實現(xiàn)邏輯,是創(chuàng)建了一個新的int對象,并返回。
不知道細心的你有沒有發(fā)現(xiàn)一個特別的地方?
id(4)的值等于id(3+1) 。這個只是python對int,和bool做的特殊優(yōu)化。不要以為其他基本類型只要值一樣都會指向相同的對象。
有個特殊的例子,str。做個簡單的實驗:

> a = "hello"
> id(a)
4365413232
> b = "hell"
> id(b)
4365386208

> id(a[:-1])
4365410928
> id(a[:-1])
4365413760

看到了嗎?雖然值相同,但是還是指向(創(chuàng)建)了不同的對象,尤其是最后兩句,哪怕執(zhí)行相同的操作,依然創(chuàng)建了不同的對象。
python這么傻,每次都創(chuàng)建新的對象?
no no no 他只是緩存了“一些”結(jié)果。我們可以再試試看:

> a = "hello"
> ret = set()
> for i in range(1000):
  ret.add(id(a[:-1]))
> print ret
{4388133312, 4388204640}

看到了嗎?python還是挺聰明的。不過具體的緩存機制我沒有深究過,期望有同學(xué)能分享下。
再次回到我們的主題,python中參數(shù)是如何傳遞的?
答案是,引用傳遞。
平時使用靜態(tài)語言的同學(xué)(比如我),可能會用下面的例子挑戰(zhàn)我了:

def fun(data):
  data = 3

a = 100
func(a)

print a # 100

不是尼瑪引用傳遞嗎?為毛在執(zhí)行func(a)后,a 的值沒有改變呢?這里犯了一個動態(tài)語言基本的錯誤。
data=3,語義上是動態(tài)語言的賦值語句。千萬不要和C++之類的語言一個理解。
看看我們傳入一個mutable 的對象:

> def func(m):
  m[3] = 100

> a = {}
> print a
{}
> func(a)
> print a
{3:100}

現(xiàn)在同學(xué)們知道該如何進行參數(shù)傳遞了吧?好嘞,進階!
像很多語言如C++,js,swift... 一樣,python 的函數(shù)聲明支持默認參數(shù):
def func(a=[]): pass
不知道什么意思?自己看書去!
我這里要說的是,如果我們的默認參數(shù)是mutable類型的對象,會有什么黑魔法產(chǎn)產(chǎn)生?
我們看看下面的函數(shù):

def func(a=[]):
  a.append(3)
  return a

可能有同學(xué)會說了:我去!這么簡單?來騙代碼的吧?
但是,真的這么簡單嗎?我們看下下面的調(diào)用結(jié)果:

> print func()
[3]
> print func()
[3,3]
> print func()
[3,3,3]

這真的是你想要的結(jié)果嗎?
No,我要的是[3],[3],[3]!
原因?好吧,我們再用下id()神奇看看:

def func(a=[]):
  print id(a)
  a.append(3)
  return a

> print func()
4365426272
[3]
> print func()
4365426272
[3, 3]
> print func()
4365426272
[3, 3, 3]

明白沒?原來在python中,*默認參數(shù)不是每次執(zhí)行時都創(chuàng)建的!*
這下你再想想,曾經(jīng)嘲笑過的代碼(至少我)為什么要 多此一舉:

def func(a=None):
  if a is None:
    a = []

這里在順帶提一下==, is:
== : 值比較
is : 比較左右兩邊是否是同一個對象。 a is b ==> id(a) == id(b)
ok, let's move on!
我們都知道,在python中,不定參數(shù)我們可以這樣定義:
def func(*args, **kv): pass
什么你不知道?看書去!
那args和kv到底是什么情況呢?到底是mutable 還是 immutable 呢?
再一次請出id()神器:

def func(*args):
  print id(args)


> a = [1,2]
> print id(a)
4364874816
> func(*a)
4364698832
> func(*a)
4364701496

看到了吧?實際上args也會產(chǎn)生一個新的對象。但是值是填入的傳入?yún)?shù)。那么每一個item也會復(fù)制嗎?
我們再看看:

def func(*args):
  print id(args[0])

> a = [1,2]
> print id(a[0])
140248135804216
> func(*a)
140248135804216

答案是,No。值會像普通list賦值一樣,指向原先list(a)所引用的對象。
那么為什么會這樣呢?
python的源碼就是這么寫的.......
最最后,還記得我說過的一句話嗎?
immutable 限制的是對象本身不可變
意思就是說,對象的immtable 只是限制自身的屬性能否被改變,而不會影響到其引用的對象。
看下下面的例子:

> a = [1,2]
> b = (a,3)
> b[1] = 100
TypeError: 'tuple' object does not support item assignment

> print b
([1, 2], 3)
> b[0][0] = 10
> print b
([10, 2], 3)

最最最后,我有個對象,它本身應(yīng)該是 mutable 的,但是我想讓他具備類似immutable的特性,可以嗎?
答案是,可以模擬!
還是之前說的,immutable 限制的是其自身屬性不能改變。
那么,我們的可以通過重定義(重載)屬性改變函數(shù),來模擬immutable特性。
python可以嗎?O~Yee
在python的類函數(shù)中,有這樣的兩個函數(shù): __setattr__ 和 __delattr__。分別會在對象屬性賦值和刪除時執(zhí)行。
那么我們可以進行簡單重載來模擬immutable:

class A:
  def __setattr__(self, name, val):
    raise TypeError("immutable object could not set attr")

以上就是為大家介紹的python黑魔法,希望對大家的學(xué)習(xí)有所幫助。

相關(guān)文章

最新評論