python中,所有的元素都是對象,其中第一類對象的通用特性:可作為值傳遞,賦值給另一個對象;可以作為元素添加到集合對象中;可以作為參數(shù)傳遞給其他函數(shù);可以作為函數(shù)的返回值
創(chuàng)新互聯(lián)是工信部頒發(fā)資質IDC服務器商,為用戶提供優(yōu)質的德陽服務器托管服務
函數(shù)就是一個callable的對象,所有對象只要你實現(xiàn)了它的call方法就跟函數(shù)一樣
python函數(shù)傳對象對性能有影響。在Python中,一切皆對象,Python參數(shù)傳遞采用的都是“傳對象引用”的方式。實際上,這種方式相當于傳值和傳引用的一種綜合。如果函數(shù)收到的是一個可變對象(比如字典或者列表)的引用,就能修改對象的原始值,相當于通過“傳引用”來傳遞對象。如果函數(shù)收到的是一個不可變對象(比如數(shù)字、字符或者元組)的引用,就不能直接修改原始對象,相當于通過“傳值’來傳遞對象,此時如果想改變這些變量的值,可以將這些變量申明為全局變量。
眾所周知,Python是一門面向對象的語言,在Python無論是數(shù)值、字符串、函數(shù)亦或是類型、類,都是對象。
對象是在 堆 上分配的結構,我們定義的所有變量、函數(shù)等,都存儲于堆內存,而變量名、函數(shù)名則是一個存儲于 棧 中、指向堆中具體結構的引用。
要想深入學習Python,首先需要知道Python對象的定義。
我們通常說的Python都是指CPython,底層由C語言實現(xiàn),源碼地址: cpython [GitHub]
Python對象的定義位于 Include/object.h ,是一個名為 PyObject 的結構體:
Python中的所有對象都繼承自PyObejct,PyObject包含一個用于垃圾回收的雙向鏈表,一個引用計數(shù)變量 ob_refcnt 和 一個類型對象指針 ob_type
從PyObejct的注釋中,我們可以看到這樣一句:每個指向 可變大小Python對象 的指針也可以轉換為 PyVarObject* (可變大小的Python對象會在下文中解釋)。 PyVarObejct 就是在PyObject的基礎上多了一個 ob_size 字段,用于存儲元素個數(shù):
在PyObject結構中,還有一個類型對象指針 ob_type ,用于表示Python對象是什么類型,定義Python對象類型的是一個 PyTypeObject 接口體
實際定義是位于 Include/cpython/object.h 的 _typeobject :
在這個類型對象中,不僅包含了對象的類型,還包含了如分配內存大小、對象標準操作等信息,主要分為:
以Python中的 int類型 為例,int類型對象的定義如下:
從PyObject的定義中我們知道,每個對象的 ob_type 都要指向一個具體的類型對象,比如一個數(shù)值型對象 100 ,它的ob_type會指向 int類型對象PyLong_Type 。
PyTypeObject結構體第一行是一個PyObject_VAR_HEAD宏,查看宏定義可知PyTypeObject是一個變長對象
也就是說,歸根結底 類型對象也是一個對象 ,也有ob_type屬性,那 PyLong_Type 的 ob_type 是什么呢?
回到PyLong_Type的定義,第一行 PyVarObject_HEAD_INIT(PyType_Type, 0) ,查看對應的宏定義
由以上關系可以知道, PyVarObject_HEAD_INIT(PyType_Type, 0) = { { _PyObject_EXTRA_INIT 1, PyType_Type } 0} ,將其代入 PyObject_VAR_HEAD ,得到一個變長對象:
這樣看就很明確了,PyLong_Type的類型就是PyType_Typ,同理可知, Python類型對象的類型就是PyType_Type ,而 PyType_Type對象的類型是它本身
從上述內容中,我們知道了對象和對象類型的定義,那么根據(jù)定義,對象可以有以下兩種分類
Python對象定義有 PyObject 和 PyVarObject ,因此,根據(jù)對象大小是否可變的區(qū)別,Python對象可以劃分為 可變對象(變長對象) 和 不可變對象(定長對象)
原本的對象a大小并沒有改變,只是s引用的對象改變了。這里的對象a、對象b就是定長對象
可以看到,變量l仍然指向對象a,只是對象a的內容發(fā)生了改變,數(shù)據(jù)量變大了。這里的對象a就是變長對象
由于存在以上特性,所以使用這兩種對象還會帶來一種區(qū)別:
聲明 s2 = s ,修改s的值: s = 'new string' ,s2的值不會一起改變,因為只是s指向了一個新的對象,s2指向的舊對象的值并沒有發(fā)生改變
聲明 l2 = l ,修改l的值: l.append(6) ,此時l2的值會一起改變,因為l和l2指向的是同一個對象,而該對象的內容被l修改了
此外,對于 字符串 對象,Python還有一套內存復用機制,如果兩個字符串變量值相同,那它們將共用同一個對象:
對于 數(shù)值型 對象,Python會默認創(chuàng)建0~2 8 以內的整數(shù)對象,也就是 0 ~ 256 之間的數(shù)值對象是共用的:
按照Python數(shù)據(jù)類型,對象可分為以下幾類:
Python創(chuàng)建對象有兩種方式,泛型API和和類型相關的API
這類API通常以 PyObject_xxx 的形式命名,可以應用在任意Python對象上,如:
使用 PyObjecg_New 創(chuàng)建一個數(shù)值型對象:
這類API通常只能作用于一種類型的對象上,如:
使用 PyLong_FromLong 創(chuàng)建一個數(shù)值型對象:
在我們使用Python聲明變量的時候,并不需要為變量指派類型,在給變量賦值的時候,可以賦值任意類型數(shù)據(jù),如:
從Python對象的定義我們已經可以知曉造成這個特點的原因了,Python創(chuàng)建對象時,會分配內存進行初始化,然后Python內部通過 PyObject* 變量來維護這個對象,所以在Python內部各函數(shù)直接傳遞的都是一種泛型指針 PyObject* ,這個指針所指向的對象類型是不固定的,只能通過所指對象的 ob_type 屬性動態(tài)進行判斷,而Python正是通過 ob_type 實現(xiàn)了多態(tài)機制
Python在管理維護對象時,通過引用計數(shù)來判斷內存中的對象是否需要被銷毀,Python中所有事物都是對象,所有對象都有引用計數(shù) ob_refcnt 。
當一個對象的引用計數(shù)減少到0之后,Python將會釋放該對象所占用的內存和系統(tǒng)資源。
但這并不意味著最終一定會釋放內存空間,因為頻繁申請釋放內存會大大降低Python的執(zhí)行效率,因此Python中采用了內存對象池的技術,是的對象釋放的空間會還給內存池,而不是直接釋放,后續(xù)需要申請空間時,優(yōu)先從內存對象池中獲取。
函數(shù)作為第一類對象(First-Class Object)卻是 Python 函數(shù)的一大特性。那究竟什么是第一類對象呢?
在 Python 中萬物皆為對象,函數(shù)也不例外,函數(shù)作為對象可以賦值給一個變量、可以作為元素添加到集合對象中、可作為參數(shù)值傳遞給其它函數(shù),還可以當做函數(shù)的返回值,這些特性就是第一類對象所特有的。
正確理解 Python函數(shù),能夠幫助我們更好地理解 Python 裝飾器、匿名函數(shù)(lambda)、函數(shù)式編程等高階技術。先來看一個簡單的例子
def foo(text):
... return len(text)
...
foo("zen of python")
13
這是一個再簡單不過的函數(shù),用于計算參數(shù) text 的長度,調用函數(shù)就是函數(shù)名后面跟一個括號,再附帶一個參數(shù),返回值是一個整數(shù)。
函數(shù)是對象
函數(shù)身為一個對象,擁有對象模型的三個通用屬性:id、類型、和值。
id(foo)
4361313816
type(foo)
class 'function'
foo
function foo at 0x103f45e18
作為對象,函數(shù)可以賦值給一個變量
bar = foo
賦值給另外一個變量時,函數(shù)并不會被調用,僅僅是在函數(shù)對象上綁定一個新的名字而已。
bar("zen of python")
13
同理,你還可以把該函數(shù)賦值給更多的變量,唯一變化的是該函數(shù)對象的引用計數(shù)不斷地增加,本質上這些變量最終指向的都是同一個函數(shù)對象。
a = foo
函數(shù)可以存儲在容器
容器對象(list、dict、set等)中可以存放任何對象,包括整數(shù)、字符串,函數(shù)也可以作存放到容器對象中,例如
funcs = [foo, str, len]
foo 是我們自定義的函數(shù),str 和 len 是兩個內置函數(shù)。for 循環(huán)逐個地迭代出列表中的每個元素時,函數(shù)對象賦值給了 f 變量,調用 f(“hello”) 與 調用 foo(“hello”) 本質是一樣的效果,每次 f 都重新指向一個新的函數(shù)對象。當然,你也可以使用列表的索引定位到元素來調用函數(shù)。
funcs[0]("Python之禪")
# 等效于 foo("Python之禪")
8
函數(shù)可以作為參數(shù)
函數(shù)還可以作為參數(shù)值傳遞給另外一個函數(shù),例如:
def show(func):
... size = func("python 之禪") # 等效于 foo("Python之禪")
... print ("length of string is : %s" % size)
...
show(foo)
length of string is : 9
函數(shù)可以作為返回值
函數(shù)作為另外一個函數(shù)的返回值,例如:
def nick():
還可以簡寫為
nick()("python")
函數(shù)接受一個或多個函數(shù)作為輸入或者函數(shù)輸出(返回)的值是函數(shù)時,我們稱這樣的函數(shù)為高階函數(shù),比如上面的 show 和 nick 都屬于高階函數(shù)。
Python內置函數(shù)中,典型的高階函數(shù)是 map 函數(shù),map 接受一個函數(shù)和一個迭代對象作為參數(shù),調用 map 時,依次迭代把迭代對象的元素作為參數(shù)調用該函數(shù)。
map(foo, ["the","zen","of","python"])
lens = map(foo, ["the","zen","of","python"])
list(lens)
[3, 3, 2, 6]
map 函數(shù)的作用相當于:
[foo(i) for i in ["the","zen","of","python"]]
[3, 3, 2, 6]
只不過 map 的運行效率更快一點。
函數(shù)可以嵌套
Python還允許函數(shù)中定義函數(shù),這種函數(shù)叫嵌套函數(shù)。
def get_length(text):
... def clean(t): # 2
... return t[1:]
... new_text = clean(text) # 1
... return len(new_text)
...
get_length("python")
5
這個函數(shù)的目的是去除字符串的第一個字符后再計算它的長度,盡管函數(shù)本身的意義不大,但能足夠說明嵌套函數(shù)。get_length 調用時,先執(zhí)行1處代碼,發(fā)現(xiàn)有調用 clean 函數(shù),于是接著執(zhí)行2中的代碼,把返回值賦值給了 new_text ,再繼續(xù)執(zhí)行后續(xù)代碼。
clean("python")
Traceback (most recent call last):
File "stdin", line 1, in module
NameError: name 'clean' is not defined
函數(shù)中里面嵌套的函數(shù)不能在函數(shù)外面訪問,只能是在函數(shù)內部使用,超出了外部函數(shù)的做用域就無效了。
實現(xiàn)了 __call__ 的類也可以作為函數(shù)
對于一個自定義的類,如果實現(xiàn)了 __call__ 方法,那么該類的實例對象的行為就是一個函數(shù),是一個可以被調用(callable)的對象。例如:
class Add:
def __init__(self, n):
self.n = n
def __call__(self, x):
return self.n + x
add = Add(1)
add(4)
5
執(zhí)行 add(4) 相當于調用 Add.__call__(add, 4),self 就是實例對象 add,self.n 等于 1,所以返回值為 1+4
add(4)
確定對象是否為可調用對象可以用內置函數(shù)callable來判斷。
callable(foo)
True
callable(1)
False
callable(int)
True
總結
Python中包含函數(shù)在內的一切皆為對象,函數(shù)作為第一類對象,支持賦值給變量,作為參數(shù)傳遞給其它函數(shù),作為其它函數(shù)的返回值,支持函數(shù)的嵌套,實現(xiàn)了__call__方法的類實例對象也可以當做函數(shù)被調用。
類,class,用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
函數(shù),是組織好的,可重復使用的,用來實現(xiàn)單一,或相關聯(lián)功能的代碼段。
函數(shù)能提高應用的模塊性,和代碼的重復利用率。你已經知道python提供了許多內建函數(shù),比如print()。但你也可以自己創(chuàng)建函數(shù),這被叫作用戶自定義函數(shù)。
python語言中類和函數(shù)的區(qū)別
1、規(guī)則不同
類是一種引用數(shù)據(jù)類型,類似于byte、short、int(char)、long、float、double等基本數(shù)據(jù)類型;
函數(shù)必須聲明后才可以被調用,調用格式為:函數(shù)名(實參)調用時函數(shù)名后的小括號中的實參必須和聲明函數(shù)時的函數(shù)括號中的形參個數(shù)相同。
2、主體不同
類是面向對象程序設計實現(xiàn)信息封裝的基礎;
函數(shù)是指一段在一起的、可以做某一件事的子程序。
3、特點不同
類是一種用戶定義的引用數(shù)據(jù)類型,也稱類類型,每個類包含數(shù)據(jù)說明和一組操作數(shù)據(jù)或者傳遞消息的函數(shù),類的實例稱為對象;
函數(shù)分為全局函數(shù)、全局靜態(tài)函數(shù),在類中還可以定義構造函數(shù)、析構函數(shù)、拷貝構造函數(shù)、成員函數(shù)、友元函數(shù)、運算符重載函數(shù)、內聯(lián)函數(shù)等。