天天看點

Python回顧與整理12:執行環境

    作為《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>&gt;&gt;&gt; </code><code>type</code><code>(</code><code>dir</code><code>)</code>

<code>&lt;</code><code>type</code> <code>'builtin_function_or_method'</code><code>&gt;</code>

<code>&gt;&gt;&gt; </code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(</code><code>'xpleaf'</code><code>.upper)</code>

    但是應用于工廠函數,得到的結果是不一樣的,雖然這些工廠函數本身是内建函數:

<code>&gt;&gt;&gt; </code><code>type</code><code>(</code><code>int</code><code>)</code>

<code>&lt;</code><code>type</code> <code>'type'</code><code>&gt;</code>

    那是因為,本質上這些工廠函數是類。

使用者定義的函數(UDF)

    使用者定義的函數通常是用Python寫的,調用type()的結果如下:

<code>&gt;&gt;&gt; </code><code>def</code> <code>foo():</code><code>pass</code>

<code>... </code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(foo)</code>

<code>&lt;</code><code>type</code> <code>'function'</code><code>&gt;</code>

    UDF本身也有很多屬性:

<code>&gt;&gt;&gt; </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表達式(名為“&lt;lambdda&gt;”的函數)

    lambda傳回一個函數對象,隻是lambda表達式沒有給指令綁定的代碼提供基礎結構,是以要通過函數式程式設計接口來調用,或把它們的引用指派給一個變量,通過該變量來進行調用,隻是需要注意的是,這個變量僅僅是個别名,并不是函數對象的名字。

    通過lambda來建立的函數對象除了沒有命名外,和使用者定義的函數是有相同的屬性的,當然,對于__name__屬性,值為"&lt;lambda&gt;":

6

7

8

9

10

11

<code>&gt;&gt;&gt; lambdaFunc </code><code>=</code> <code>lambda</code> <code>x: x</code><code>*</code><code>2</code>

<code>&gt;&gt;&gt; lambdaFunc(</code><code>100</code><code>)</code>

<code>200</code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(lambdaFunc)</code>

<code>&gt;&gt;&gt; lambdaFunc.__name__</code>

<code>'&lt;lambda&gt;'</code>

<code>&gt;&gt;&gt; foo.__name__</code>

<code>'foo'</code>

(2)方法

    使用者自定義方法是被定義為類的一部分函數,而許多Python資料類型本身也有方法,這些方法被稱為内建方法。

内建方法(BIM)

    隻有内建類型有内建方法,如下:

<code>&gt;&gt;&gt; </code><code>type</code><code>([].append)</code>

    BIM和BIF兩者享有相同的屬性,隻是不同的是,BIM的__self__屬性指向一個Python對象,而BIF指向None:

<code>&gt;&gt;&gt; </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>&gt;&gt;&gt; [].append.__self__</code>

<code>[]</code>

<code>&gt;&gt;&gt; </code><code>dir</code><code>.__self__</code>

<code>&gt;&gt;&gt; </code><code>print</code> <code>[].append.__self__</code>

<code>&gt;&gt;&gt; </code><code>print</code> <code>dir</code><code>.__self__</code>

<code>None</code>

使用者定義的方法(UDM)

    所有的UDM都是相同的類型,即“執行個體方法”,無論該方法是否有綁定的執行個體:

<code>&gt;&gt;&gt; </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>&gt;&gt;&gt; c </code><code>=</code> <code>C()</code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(C.foo)</code>

<code>&lt;</code><code>type</code> <code>'instancemethod'</code><code>&gt;</code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(c.foo)</code>

    隻是通路對象本身會揭示該方法是否綁定:

<code>&gt;&gt;&gt; c.foo</code>

<code>&lt;bound method C.foo of &lt;__main__.C </code><code>object</code> <code>at </code><code>0x7f37a3c45810</code><code>&gt;&gt;</code>

<code>&gt;&gt;&gt; C.foo</code>

<code>&lt;unbound method C.foo&gt;</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>&gt;&gt;&gt; c</code>

<code>&lt;__main__.C </code><code>object</code> <code>at </code><code>0x7f37a3c2d090</code><code>&gt;</code>

<code>&gt;&gt;&gt; </code><code>callable</code><code>(c)    </code><code># 執行個體是可調用的</code>

<code>True</code>

<code>&gt;&gt;&gt; c()    </code><code># 調用執行個體</code>

<code>I'm </code><code>callable</code><code>! Called with args:</code>

<code>()</code>

<code>&gt;&gt;&gt; c(</code><code>3</code><code>)    </code><code># 傳入參數</code>

<code>(</code><code>3</code><code>,)</code>

<code>&gt;&gt;&gt; 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>&gt;&gt;&gt; c.__call__(</code><code>3</code><code>)    </code><code># 與c(3)的調用原理是一樣的</code>

<code>&gt;&gt;&gt; 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>&gt;&gt;&gt; </code><code>def</code> <code>foo(): </code><code>pass</code>

<code>&gt;&gt;&gt; foo.func_code</code>

<code>&lt;code </code><code>object</code> <code>foo at </code><code>0x7f37a5daceb0</code><code>, </code><code>file</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>1</code><code>&gt;</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>&gt;&gt;&gt; </code><code>callable</code><code>(</code><code>dir</code><code>)</code>

<code>&gt;&gt;&gt; </code><code>callable</code><code>(</code><code>1</code><code>)</code>

<code>False</code>

<code>&gt;&gt;&gt; </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>&gt;&gt;&gt; eval_code </code><code>=</code> <code>compile</code><code>(</code><code>'100+200'</code><code>, '</code><code>', '</code><code>eval</code><code>')</code>

<code>&gt;&gt;&gt; eval_code</code>

<code>&lt;code </code><code>object</code> <code>&lt;module&gt; at </code><code>0x7f37a5db3cb0</code><code>, </code><code>file</code> <code>"", line </code><code>1</code><code>&gt;</code>

<code>&gt;&gt;&gt; </code><code>eval</code><code>(eval_code)</code>

<code>300</code>

單一可執行語句

<code>&gt;&gt;&gt; single_code </code><code>=</code> <code>compile</code><code>(</code><code>'print "Hello world!"'</code><code>, '</code><code>', '</code><code>single')</code>

<code>&gt;&gt;&gt; single_code</code>

<code>&lt;code </code><code>object</code> <code>&lt;module&gt; at </code><code>0x7f37a3c2a0b0</code><code>, </code><code>file</code> <code>"", line </code><code>1</code><code>&gt;</code>

<code>&gt;&gt;&gt; </code><code>exec</code> <code>single_code</code>

<code>Hello world!</code>

可執行語句組

<code>&gt;&gt;&gt; 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>&gt;&gt;&gt; </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>&gt;&gt;&gt; a </code><code>=</code> <code>3</code>

<code>&gt;&gt;&gt; </code><code>eval</code><code>(</code><code>'a'</code><code>)</code>

<code>&gt;&gt;&gt; </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>&gt;&gt;&gt; test_eval()</code>

<code>6</code>

<code>&gt;&gt;</code>

<code>&gt;&gt;&gt; scope</code><code>=</code><code>{}</code>

<code>&gt;&gt;&gt; scope[</code><code>'a'</code><code>] </code><code>=</code> <code>3</code>

<code>&gt;&gt;&gt; scope[</code><code>'b'</code><code>] </code><code>=</code> <code>4</code>

<code>&gt;&gt;&gt; result </code><code>=</code> <code>eval</code><code>(</code><code>'a+b'</code><code>,scope)</code>

<code>&gt;&gt;&gt; result</code>

<code>7</code>

(4)exec

    exec語句隻接受一個參數,即exec obj,obj除了可以是原始的字元串(單一語句或語句組)或是compile()編譯的代碼對象外,也可以是檔案對象:

<code>&gt;&gt;&gt; f </code><code>=</code> <code>open</code><code>(</code><code>'xcount.py'</code><code>)</code>

<code>&gt;&gt;&gt; </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 &lt; </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>&gt;&gt;&gt; f.tell()</code>

<code>86</code>

<code>&gt;&gt;&gt; f.seek(</code><code>0</code><code>)</code>

<code>&gt;&gt;&gt; </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>&gt;&gt;&gt; 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>&gt;&gt;&gt; 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>&gt;&gt;&gt; aString</code>

<code>"['xpleaf', 'clyyh']"</code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(aString)</code>

<code>&lt;</code><code>type</code> <code>'str'</code><code>&gt;</code>

<code>&gt;&gt;&gt; aList </code><code>=</code> <code>input</code><code>(</code><code>'Enter a list: '</code><code>)</code>

<code>&gt;&gt;&gt; aList</code>

<code>[</code><code>'xpleaf'</code><code>, </code><code>'clyyh'</code><code>]</code>

<code>&gt;&gt;&gt; </code><code>type</code><code>(aList)</code>

<code>&lt;</code><code>type</code> <code>'list'</code><code>&gt;</code>

(6)使用Python在運作時生成和執行Python代碼

    有需要時可以參考書本上的例子。

本文轉自 xpleaf 51CTO部落格,原文連結:http://blog.51cto.com/xpleaf/1795129,如需轉載請自行聯系原作者