作為《Python核心程式設計》核心部分的最後一章,這篇的内容也相當重要。對于進階部分的整理,将采用《Python核心程式設計》第三版,但是,方式會以之前的完全不一樣了。
1.可調用對象
可調用對象即可通過函數操作符“()”來調用的對象,也可以通過函數式程式設計接口來進行調用,如apply()、filter()、map()和reduce()。Python有4種可調用對象:函數、方法、類和一些類的執行個體。
(1)函數
Python中有三種不同類型的函數:内建函數(BIF)、使用者定義的函數(UDF)和lambda表達式。
内建函數(BIF)
用C/C++寫的,編譯過後放入Python解釋器中,然後把它們作為第一(内建)名稱空間的一部分加載進系統,這些函數在__builtin__子產品裡,并作為__builtins__子產品導入到解釋器中。
可以使用dir()來列出函數的所有屬性,另外從内部機制來看,BIF和内建方法屬于相同的類型,是以全長type()調用的結果是:
1
2
3
4
5
<code>>>> </code><code>type</code><code>(</code><code>dir</code><code>)</code>
<code><</code><code>type</code> <code>'builtin_function_or_method'</code><code>></code>
<code>>>> </code>
<code>>>> </code><code>type</code><code>(</code><code>'xpleaf'</code><code>.upper)</code>
但是應用于工廠函數,得到的結果是不一樣的,雖然這些工廠函數本身是内建函數:
<code>>>> </code><code>type</code><code>(</code><code>int</code><code>)</code>
<code><</code><code>type</code> <code>'type'</code><code>></code>
那是因為,本質上這些工廠函數是類。
使用者定義的函數(UDF)
使用者定義的函數通常是用Python寫的,調用type()的結果如下:
<code>>>> </code><code>def</code> <code>foo():</code><code>pass</code>
<code>... </code>
<code>>>> </code><code>type</code><code>(foo)</code>
<code><</code><code>type</code> <code>'function'</code><code>></code>
UDF本身也有很多屬性:
<code>>>> </code><code>dir</code><code>(foo)</code>
<code>[</code><code>'__call__'</code><code>, </code><code>'__class__'</code><code>, </code><code>'__closure__'</code><code>, </code><code>'__code__'</code><code>, </code><code>'__defaults__'</code><code>, </code><code>'__delattr__'</code><code>, </code><code>'__dict__'</code><code>, </code><code>'__doc__'</code><code>, </code><code>'__format__'</code><code>, </code><code>'__get__'</code><code>, </code><code>'__getattribute__'</code><code>, </code><code>'__globals__'</code><code>, </code><code>'__hash__'</code><code>, </code><code>'__init__'</code><code>, </code><code>'__module__'</code><code>, </code><code>'__name__'</code><code>, </code><code>'__new__'</code><code>, </code><code>'__reduce__'</code><code>, </code><code>'__reduce_ex__'</code><code>, </code><code>'__repr__'</code><code>, </code><code>'__setattr__'</code><code>, </code><code>'__sizeof__'</code><code>, </code><code>'__str__'</code><code>, </code><code>'__subclasshook__'</code><code>, </code><code>'func_closure'</code><code>, </code><code>'func_code'</code><code>, </code><code>'func_defaults'</code><code>, </code><code>'func_dict'</code><code>, </code><code>'func_doc'</code><code>, </code><code>'func_globals'</code><code>, </code><code>'func_name'</code><code>]</code>
lambda表達式(名為“<lambdda>”的函數)
lambda傳回一個函數對象,隻是lambda表達式沒有給指令綁定的代碼提供基礎結構,是以要通過函數式程式設計接口來調用,或把它們的引用指派給一個變量,通過該變量來進行調用,隻是需要注意的是,這個變量僅僅是個别名,并不是函數對象的名字。
通過lambda來建立的函數對象除了沒有命名外,和使用者定義的函數是有相同的屬性的,當然,對于__name__屬性,值為"<lambda>":
6
7
8
9
10
11
<code>>>> lambdaFunc </code><code>=</code> <code>lambda</code> <code>x: x</code><code>*</code><code>2</code>
<code>>>> lambdaFunc(</code><code>100</code><code>)</code>
<code>200</code>
<code>>>> </code><code>type</code><code>(lambdaFunc)</code>
<code>>>> lambdaFunc.__name__</code>
<code>'<lambda>'</code>
<code>>>> foo.__name__</code>
<code>'foo'</code>
(2)方法
使用者自定義方法是被定義為類的一部分函數,而許多Python資料類型本身也有方法,這些方法被稱為内建方法。
内建方法(BIM)
隻有内建類型有内建方法,如下:
<code>>>> </code><code>type</code><code>([].append)</code>
BIM和BIF兩者享有相同的屬性,隻是不同的是,BIM的__self__屬性指向一個Python對象,而BIF指向None:
<code>>>> </code><code>dir</code><code>([].append)</code>
<code>[</code><code>'__call__'</code><code>, </code><code>'__class__'</code><code>, </code><code>'__cmp__'</code><code>, </code><code>'__delattr__'</code><code>, </code><code>'__doc__'</code><code>, </code><code>'__eq__'</code><code>, </code><code>'__format__'</code><code>, </code><code>'__ge__'</code><code>, </code><code>'__getattribute__'</code><code>, </code><code>'__gt__'</code><code>, </code><code>'__hash__'</code><code>, </code><code>'__init__'</code><code>, </code><code>'__le__'</code><code>, </code><code>'__lt__'</code><code>, </code><code>'__module__'</code><code>, </code><code>'__name__'</code><code>, </code><code>'__ne__'</code><code>, </code><code>'__new__'</code><code>, </code><code>'__reduce__'</code><code>, </code><code>'__reduce_ex__'</code><code>, </code><code>'__repr__'</code><code>, </code><code>'__self__'</code><code>, </code><code>'__setattr__'</code><code>, </code><code>'__sizeof__'</code><code>, </code><code>'__str__'</code><code>, </code><code>'__subclasshook__'</code><code>]</code>
<code>>>> [].append.__self__</code>
<code>[]</code>
<code>>>> </code><code>dir</code><code>.__self__</code>
<code>>>> </code><code>print</code> <code>[].append.__self__</code>
<code>>>> </code><code>print</code> <code>dir</code><code>.__self__</code>
<code>None</code>
使用者定義的方法(UDM)
所有的UDM都是相同的類型,即“執行個體方法”,無論該方法是否有綁定的執行個體:
<code>>>> </code><code>class</code> <code>C(</code><code>object</code><code>):</code>
<code>... </code><code>def</code> <code>foo(</code><code>self</code><code>):</code>
<code>... </code><code>pass</code>
<code>>>> c </code><code>=</code> <code>C()</code>
<code>>>> </code><code>type</code><code>(C.foo)</code>
<code><</code><code>type</code> <code>'instancemethod'</code><code>></code>
<code>>>> </code><code>type</code><code>(c.foo)</code>
隻是通路對象本身會揭示該方法是否綁定:
<code>>>> c.foo</code>
<code><bound method C.foo of <__main__.C </code><code>object</code> <code>at </code><code>0x7f37a3c45810</code><code>>></code>
<code>>>> C.foo</code>
<code><unbound method C.foo></code>
(3)類
調用類,其實就是建立一個執行個體對象。類有預設構造器,但是這個函數什麼都不做,可以通過實作__init__()方法來自定義執行個體化過程。
(4)類的執行個體
Python給類提供了名為__call__的特别方法,該方法允許程式建立可調用的對象(執行個體)。預設情況下沒有實作該方法,是以執行個體是不可調用的。
在定義類時覆寫__call__方法即可達到執行個體可調用的效果,這樣,調用執行個體對象就等同于調用__call__()方法,參數的設定跟類的構造器原理是一樣的:
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<code>... </code><code>def</code> <code>__call__(</code><code>self</code><code>, </code><code>*</code><code>args):</code>
<code>... </code><code>print</code> <code>"I'm callable! Called with args:\n"</code><code>, args</code>
<code>>>> c</code>
<code><__main__.C </code><code>object</code> <code>at </code><code>0x7f37a3c2d090</code><code>></code>
<code>>>> </code><code>callable</code><code>(c) </code><code># 執行個體是可調用的</code>
<code>True</code>
<code>>>> c() </code><code># 調用執行個體</code>
<code>I'm </code><code>callable</code><code>! Called with args:</code>
<code>()</code>
<code>>>> c(</code><code>3</code><code>) </code><code># 傳入參數</code>
<code>(</code><code>3</code><code>,)</code>
<code>>>> c(</code><code>3</code><code>, </code><code>'no more , no less'</code><code>)</code>
<code>(</code><code>3</code><code>, </code><code>'no more , no less'</code><code>)</code>
<code>>>> c.__call__(</code><code>3</code><code>) </code><code># 與c(3)的調用原理是一樣的</code>
<code>>>> c.__call__(</code><code>3</code><code>, </code><code>'no more, no less'</code><code>)</code>
<code>(</code><code>3</code><code>, </code><code>'no more, no less'</code><code>)</code>
2.代碼對象
每個可調用物的核心都是代碼對象,由語句、指派、表達式和其他可調用物組成。檢視一個子產品意味着觀察一個較大的、包含了子產品中所有代碼的對象。然後代碼可以分成語句、指派、表達式,以及可調用物。可調用物又可以遞歸分解到下一層,那兒有自己的代碼對象。
一般來說,代碼對象可以作為函數或者方法調用的一部分來執行(比如語句或指派等),也可用exec語句或内建函數eval()來執行。從整體上看,一個Python子產品的代碼對象是構成該子產品的全部代碼。
如果要執行Python代碼,那麼該代碼必須先要轉換成位元組編譯的代碼(又稱位元組碼)。這才是真正的代碼對象。然而,它們不包含任何關于它們執行環境的資訊,這便是可調用物存在的原因,它被用來包裝一個代碼對象并提供額外的資訊(如屬性等相關資訊)。
函數對象僅是代碼對象的包裝,方法則是給函數對象的包裝,隻是不同于單純的代碼對象,它們還提供了一些額外的資訊。當研究到最底層,便會發現這是一個代碼對象。
在函數對象中,有一個func_code屬性,其實就是指代碼對象:
<code>>>> </code><code>def</code> <code>foo(): </code><code>pass</code>
<code>>>> foo.func_code</code>
<code><code </code><code>object</code> <code>foo at </code><code>0x7f37a5daceb0</code><code>, </code><code>file</code> <code>"<stdin>"</code><code>, line </code><code>1</code><code>></code>
3.可執行的對象聲明和内建函數
常見的相關函數如下:
可執行的對象聲明和内建函數
内建函數和語句
描述
callable(obj)
如果obj可調用,傳回True,否則傳回False
compile(string, file, type)
從type類型中建立代碼對象;file是代碼存放的地方(通常設為"")
eval(obj, globals=globals(), locals=locals())
對obj進行求值,obj是已編譯為代碼對象的表達式,或是一個字元串表達式;可以給出全局或者/和局部的名稱空間
exec obj
執行obj、單一的Python語句或者語句的集合,也就是說格式是代碼對象或者字元串;obj也可以是一個檔案對象(已經打開的有效的Python腳本中)
input(prompt='')
等同于eval(raw_input(prompt=''))
(1)callable()
callable()是一個布爾函數,确定一個對象是否可以通過函數操作符(())來調用:
<code>>>> </code><code>callable</code><code>(</code><code>dir</code><code>)</code>
<code>>>> </code><code>callable</code><code>(</code><code>1</code><code>)</code>
<code>False</code>
<code>>>> </code><code>callable</code><code>(foo)</code>
(2)compile()
compile()函數允許程式員在運作時刻迅速生成代碼對象,然後就可以用exec語句或者内建函數eval()來執行這些代碼對象或者對它們進行求值。一個很重要的觀點是:exec和eval()都可以執行字元串格式的Python代碼。當執行字元串形式的代碼時,每次都必須對這些代碼進行位元組編譯處理。compile()函數提供了一次性位元組代碼預編譯,以後每次調用的時候,就不用編譯了。
compile的三個參數都是必需的,第一參數借助了要編譯的Python代碼。第二個字元串,雖然是必需的,但通常被設定為空串。該參數代表了存放代碼對象的檔案的名字(字元串類型)。compile的通常用法是動态生成字元串形式的Python代碼,然後生成一個代碼對象——代碼顯然沒有存放在任何檔案,是以檔案名就通常設定為空了。
最後的參數是個字元串,它用來表明代碼對象的類型,有三個可能值:
'eval'
可求值的表達式,和eval()一起使用
'single'
單一可執行語句,和exec一起使用
'exec'
可執行語句組,和exec一起使用
可求值表達式
<code>>>> eval_code </code><code>=</code> <code>compile</code><code>(</code><code>'100+200'</code><code>, '</code><code>', '</code><code>eval</code><code>')</code>
<code>>>> eval_code</code>
<code><code </code><code>object</code> <code><module> at </code><code>0x7f37a5db3cb0</code><code>, </code><code>file</code> <code>"", line </code><code>1</code><code>></code>
<code>>>> </code><code>eval</code><code>(eval_code)</code>
<code>300</code>
單一可執行語句
<code>>>> single_code </code><code>=</code> <code>compile</code><code>(</code><code>'print "Hello world!"'</code><code>, '</code><code>', '</code><code>single')</code>
<code>>>> single_code</code>
<code><code </code><code>object</code> <code><module> at </code><code>0x7f37a3c2a0b0</code><code>, </code><code>file</code> <code>"", line </code><code>1</code><code>></code>
<code>>>> </code><code>exec</code> <code>single_code</code>
<code>Hello world!</code>
可執行語句組
<code>>>> exec_code </code><code>=</code> <code>compile</code><code>(</code><code>"""</code>
<code>... req = input('Count how many numbers?')</code>
<code>... for eachNum in range(req):</code>
<code>... print eachNum</code>
<code>... """</code><code>, '</code><code>', '</code><code>exec</code><code>')</code>
<code>>>> </code><code>exec</code> <code>exec_code</code>
<code>Count how many numbers?</code><code>6</code>
<code>0</code>
<code>1</code>
<code>2</code>
<code>3</code>
<code>4</code>
<code>5</code>
(3)eval()
eval()對表達式求值,第一個參數可以為字元串或compile()建立的預編譯代碼對象。第二個和第三個參數為可選的,分别預設為globals()和locals()傳回的對象:
<code>>>> a </code><code>=</code> <code>3</code>
<code>>>> </code><code>eval</code><code>(</code><code>'a'</code><code>)</code>
<code>>>> </code><code>def</code> <code>test_eval():</code>
<code>... a </code><code>=</code> <code>6</code>
<code>... </code><code>print</code> <code>eval</code><code>(</code><code>'a'</code><code>)</code>
<code>>>> test_eval()</code>
<code>6</code>
<code>>></code>
<code>>>> scope</code><code>=</code><code>{}</code>
<code>>>> scope[</code><code>'a'</code><code>] </code><code>=</code> <code>3</code>
<code>>>> scope[</code><code>'b'</code><code>] </code><code>=</code> <code>4</code>
<code>>>> result </code><code>=</code> <code>eval</code><code>(</code><code>'a+b'</code><code>,scope)</code>
<code>>>> result</code>
<code>7</code>
(4)exec
exec語句隻接受一個參數,即exec obj,obj除了可以是原始的字元串(單一語句或語句組)或是compile()編譯的代碼對象外,也可以是檔案對象:
<code>>>> f </code><code>=</code> <code>open</code><code>(</code><code>'xcount.py'</code><code>)</code>
<code>>>> </code><code>for</code> <code>eachLine </code><code>in</code> <code>f:</code>
<code>... </code><code>print</code> <code>eachLine,</code>
<code>x </code><code>=</code> <code>0</code>
<code>print</code> <code>'x is currently:'</code><code>, x</code>
<code>while</code> <code>x < </code><code>5</code><code>:</code>
<code> </code><code>x </code><code>+</code><code>=</code> <code>1</code>
<code> </code><code>print</code> <code>'incrementing x to:'</code><code>, x</code>
<code>>>> f.tell()</code>
<code>86</code>
<code>>>> f.seek(</code><code>0</code><code>)</code>
<code>>>> </code><code>exec</code> <code>f</code>
<code>x </code><code>is</code> <code>currently: </code><code>0</code>
<code>incrementing x to: </code><code>1</code>
<code>incrementing x to: </code><code>2</code>
<code>incrementing x to: </code><code>3</code>
<code>incrementing x to: </code><code>4</code>
<code>incrementing x to: </code><code>5</code>
<code>>>> f.tell() </code><code># 執行完之後,就指針就停留在檔案末尾(end-of-line,EOF)</code>
(5)input()
内建函數input()是eval()和raw_input()的組合,等價于eval(raw_input()),這說明input()和raw_input()本身還是有差別的。
從功能上看,input不同于raw_input(),因為raw_input()總是以字元串的形式傳回使用者的輸入;input()履行相同的任務,而且它還把輸入作為Python表達式進行求值。這意味着input()傳回的資料是對輸入表達式求值的結果:一個Python對象。
舉例如下:
<code>>>> aString </code><code>=</code> <code>raw_input</code><code>(</code><code>'Enter a list: '</code><code>)</code>
<code>Enter a </code><code>list</code><code>: [</code><code>'xpleaf'</code><code>, </code><code>'clyyh'</code><code>]</code>
<code>>>> aString</code>
<code>"['xpleaf', 'clyyh']"</code>
<code>>>> </code><code>type</code><code>(aString)</code>
<code><</code><code>type</code> <code>'str'</code><code>></code>
<code>>>> aList </code><code>=</code> <code>input</code><code>(</code><code>'Enter a list: '</code><code>)</code>
<code>>>> aList</code>
<code>[</code><code>'xpleaf'</code><code>, </code><code>'clyyh'</code><code>]</code>
<code>>>> </code><code>type</code><code>(aList)</code>
<code><</code><code>type</code> <code>'list'</code><code>></code>
(6)使用Python在運作時生成和執行Python代碼
有需要時可以參考書本上的例子。
本文轉自 xpleaf 51CTO部落格,原文連結:http://blog.51cto.com/xpleaf/1795129,如需轉載請自行聯系原作者