天天看點

Dave Python 練習十六 -- 執行環境

#encoding=utf-8
###***************  執行環境  *********************

##************  Part 1: 可調用對象  *****************
#許多的python 對象都是我們所說的可調用的,即是任何能通過函數操作符“()”來調用的對象。
#要調用可調用對象,函數操作符得緊跟在可調用對象之後。比方說,用“foo()”來調用函數"foo"。
#可調用對象可以通過函數式程式設計接口來進行調用,如apply(),filter(),map()。
#Python 有4 種可調用對象:函數,方法,類,以及一些類的執行個體。記住這些對象的任何引用或者别名都是可調用的。

#### 1.1  函數
#python 有3 種不同類型函數對象.

## 1.1.1 内建函數(BIFs)
#BIF 是用c/c++寫的,編譯過後放入python 解釋器,然後把它們作為第一(内建)名字空間的
#一部分加載進系統。這些函數在_bulitin_子產品裡,并作為__builtins__子產品導入到解釋器中。

#内建函數屬性
#BIF 屬性          描述
#bif.__doc__       文檔字元串(或None)
#bif.__name__      字元串類型的文檔名字
#bif.__self__      設定為None(保留給built-in 方法)
#bif.__module__    存放bif 定義的子產品名字(或None)

#可以用dir()列出函數的所有屬性:
#print(dir(type))
#['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', 
#'__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', 
#'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', 
#'__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__prepare__', 
#'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', 
#'__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']

#從内部機制來看,因為BIFs 和内建方法(BIMs)屬于相同的類型,是以對BIF 或者BIM 調用type()的結果是:
#>>> type(dir)
#<type 'builtin_function_or_method'>

#注意這不能應用于工廠函數,因為type()正好會傳回産生對象的類型:
#>>> type(int)
#<type 'type'>
#>>> type(type)
#<type 'type'>

## 1.1.2 使用者定義的函數(UDF)
#UDF(User-Defined Function,使用者定義的函數)通常是用python 寫的,定義在子產品的最進階,
#是以會作為全局名字空間的一部分(一旦建立好内建名字空間)裝載到系統中。函數也可在其他的函
#數體内定義,現在可以對多重嵌套作用域中的屬性進行通路。可以用func_closure 屬性來鈎住在其他地方定義的屬性。

#使用者自定義函數屬性
#UDF 屬性             描述
#udf.__doc__         文檔字元串(也可以用udf.func_doc)
#udf.__name__        字元串類型的函數名字(也可以用 udf.func_name)
#udf.func_code       位元組編譯的代碼對象
#udf.func_defaults   預設的參數元組
#udf.func_globals    全局名字空間字典; 和從函數内部調用globals(x)一樣
#udf.func_dict       函數屬性的名字空間
#udf.func_doc        (見上面的udf.__doc__)
#udf.func_name       (見上面的udf.__name__)
#udf.func_closure    包含了自由變量的引用的單元對象元組

#從内部機制來看,使用者自定義的函數是“函數“類型的.
#
#lambda 表達式和使用者自定義對函數相比,略有不同。雖然它們也是傳回一個函數對象,但是
#lambda 表達式不是用def 語句建立的,而是用lambda 關鍵字:
#因為lambda 表達式沒有給命名綁定的代碼提供基礎結構,是以要通過函數式程式設計接口來調用,
#或把它們的引用指派給一個變量,然後就可以直接調用或者再通過函數來調用。變量僅是個别名,
#并不是函數對象的名字。
#通過lambda 來建立函數的對象除了沒有命名之外,享有和使用者自定義函數相同的屬性;__name__
#或者func_name 屬性給定為字元串"<lambda>"。使用type()工廠函數,我們來示範下lambda 表達式
#傳回和使用者自定義函數相同的函數對象。
#
#>>> lambdaFunc = lambda x: x * 2
#>>> lambdaFunc(100)
#200
#>>> type(lambdaFunc)
#<type 'function'>

#### 1.2  方法
#使用者自定義方法是被定義為類的一部分的函數。許多python 資料
#類型,比如清單和字典,也有方法,這些被稱為内建方法。為了進一步說明“所有權“的類型,方
#法通過對象的名字和句點屬性辨別進行命名。

#内建方法(BIM)屬性
#BIM 屬性             描述
#bim.__doc__         文檔字串
#bim.__name__        字元串類型的函數名字
#bim.__self__        綁定的對象

## 1.2.1 内建方法(BIMs)
#BIM 和BIF 兩者也都享有相同屬性。不同之處在于BIM 的__self__屬性指向一個Python對象,而BIF 指向None。

#隻有内建類型(BIT)有BIM.對于内建方法,type()工廠函數給出了和BIF 相同的輸出--注意,我們是如何提供一個内建對象來通路BIM:
#>>> type([].append)
#<type 'builtin_function_or_method'>

#對于類和執行個體,都能以該對象為參數,通過内建函數dir()來獲得他們的資料和方法屬性。這也可以用在BIM 上:
#>>> dir([].append)
#['__call__', '__class__', '__cmp__', '__delattr__', '__doc__',
#'__getattribute__', '__hash__', '__init__', '__module__',
#'__name__', '__new__', '__reduce__', '__reduce_ex__',
#'__repr__', '__self__', '__setattr__', '__str__']

## 1.2.2 使用者定義的方法(UDM)
#UDM(User-defined method,使用者定義的方法)包含在類定義之中,隻是擁有标準函數的包裝,
#僅有定義它們的類可以使用。如果沒有在子類定義中被覆寫掉,也可以通過子類執行個體來調用它們。

#UDM 與類對象是關聯的(非綁定方法),但是隻能通過類的執行個體來調用(綁
#定方法)。無論UDMs 是否綁定,所有的UMD 都是相同的類型——“執行個體方法“,


#>>> class C(object): # define class # 定義類
#... def foo(self): pass # define UDM # 定義UDM
#...
#>>> c = C() # instantiation # 執行個體化
#>>> type(C) # type of class # 類的類别
#<type 'type'>
#>>> type(c) # type of instance # 執行個體的類别
#<class '__main__.C'>
#>>> type(C.foo) # type of unbound method # 非綁定方法的類别
#<type 'instancemethod'>
#>>> type(c.foo) # type of bound method # 綁定方法的類别
#<type 'instancemethod'>

#使用者自定義屬性
#UDM 屬性            描述
#udm.__doc__         文檔字元串(與udm.im_fuc.__doc__相同)
#udm.__name__        字元串類型的方法名字(與umd.im_func.__name__相同)
#udm.__module__      定義udm 的子產品的名字(或none)
#udm.im_class        方法相關聯的類(對于綁定的方法;如果是非綁定,那麼為要求udm 的類)
#udm.im_func         方法的函數對象(見UDFs)
#udm.im_self         如果綁定的話為相關聯的執行個體,如果非綁定位為none

#### 1.3 類
#可以利用類的可調用性來建立執行個體。“調用”類的結果便是建立了執行個體,即大家所知道的實
#例化。類有預設構造函數,該函數什麼都不做,基本上隻有一個pass 語句。程式員可以通過實作
#__int__()方法,來自定義執行個體化過程。執行個體化調用的任何參數都會傳入到構造函數裡。

#### 1.4 類的執行個體
#python 給類提供了名為__call__的特别方法,該方法允許程式員建立可調用的對象(執行個體)。默
#認情況下,__call__()方法是沒有實作的,這意味着大多數執行個體都是不可調用的。然而,如果在類
#定義中覆寫了這個方法,那麼這個類的執行個體就成為可調用的了。調用這樣的執行個體對象等同于調用
#__call__()方法.任何在執行個體調用中給出的參數都會被傳入到__call()__中.

#那麼foo()就和foo.__call__(foo)的效果相同, 這裡foo 也作為參數出現,因為是對自己的引用,執行個體将自
#動成為每次方法調用的第一個參數。如果 ___call___()有參數,比如,(self, arg),那麼foo(arg)就和調用foo.__call__(foo, arg)一樣。

#class C(object):
#    def __call__(self, *args):
#        print("I'm callable! Called with args:\n", args)


##************  Part 2: 代碼對象  *****************
#可調用的對象是python 執行環境裡最重要的部分,然而他們隻是冰山一角。python 語句,指派,
#表達式,甚至還有子產品構成了更宏大的場面。這些可執行對象無法像可調用物那樣被調用。更确切
#地說,這些對象隻是構成可執行代碼塊的拼圖的很小一部分,而這些代碼塊被稱為代碼對象。

#每個可調用物的核心都是代碼對象,由語句,指派,表達式,以及其他可調用物組成。察看一
#個子產品意味着觀察一個較大的、包含了子產品中所有代碼的對象。然後代碼可以分成語句,指派,表
#達式,以及可調用物。可調用物又可以遞歸分解到下一層,那兒有自己的代碼對象。

#一般說來,代碼對象可以作為函數或者方法調用的一部分來執行,也可用exec 語句或内建函數
#eval()來執行。從整體上看,一個python 子產品的代碼對象是構成該子產品的全部代碼。

#如果要執行python 代碼,那麼該代碼必須先要轉換成位元組編譯的代碼(又稱位元組碼)。這才是
#真正的代碼對象。然而,它們不包含任何關于它們執行環境的資訊,這便是可調用物存在的原因,
#它被用來包裝一個代碼對象并提供額外的資訊。


##************  Part 3: 可執行的對象聲明和内建函數  *****************
#Python 提供了大量的BIF 來支援可調用/可執行對象,其中包括exec 語句。這些函數幫助程式
#員執行代碼對象,也可以用内建函數complie()來生成代碼對象。

#内建函數和語句                                   描述
#callable(obj)                                   如果obj 可調用,傳回True,否則傳回FALSE
#compile(string,file, type)                      從type 類型中建立代碼對象;file 是代碼存放的地方(通常設為"")
#eval(obj, glo- bals=globals(),locals=locals())  對obj 進行求值,obj 是已編譯為代碼對象的表達式,或是一個字元串表達式;可以給出全局或者/和局部的名字空間
#exec obj                                        執行obj、單一的python 語句或者語句的集合,也就是說格式是代碼對象或者字元串;obj 也可以是一個檔案對象(已經打開的有
#效python 腳本中)
#input(prompt='')                                等同于eval(raw_input(prompt=”))

### 3.1 callable()
#callable()是一個布爾函數,确定一個對象是否可以通過函數操作符(())來調用。如果函數可調用便傳回True,否則便是False.
#print(callable(dir))
#print(callable(1))
#-->
#True
#False

### 3.2 compile()
#compile()函數允許程式員在運作時刻迅速生成代碼對象,然後就可以用exec 語句或者内建函
#數eval()來執行這些對象或者對它們進行求值。一個很重要的觀點是:exec 和eval()都可以執行字
#符串格式的Python 代碼。當執行字元串形式的代碼時,每次都必須對這些代碼進行位元組編譯處理。
#compile()函數提供了一次性位元組代碼預編譯,以後每次調用的時候,都不用編譯了

#compile 的三個參數都是必需的,第一參數代表了要編譯的python 代碼。第二個字元串,雖然
#是必需的,但通常被置為空串。該參數代表了存放代碼對象的檔案的名字(字元串類型)。compile 的
#通常用法是動态生成字元串形式的Python 代碼, 然後生成一個代碼對象——代碼顯然沒有存放在任何檔案。
#
#最後的參數是個字元串,它用來表明代碼對象的類型。有三個可能值:
#'eval' 可求值的表達式[和eval()一起使用]
#'single' 單一可執行語句[和exec 一起使用]
#'exec' 可執行語句組[和exec 一起使用]

#可求值表達式
#eval_code = compile('100 + 200', '', 'eval')
#print(eval(eval_code))
#-->
#300

#單一可執行語句
#single_code = compile('print("Hello world!")', '', 'single')
#print(single_code)
#exec(single_code)
#-->
#<code object <module> at 0x0000000002275718, file "", line 1>
#Hello world!

#可執行語句組
#exec_code = compile("""
#req = input('Count how many numbers? ')
#for eachNum in range(int(req)):
#    print(eachNum)
#""", '', 'exec')
#exec(exec_code)
#-->
#Count how many numbers? 4
#0
#1
#2
#3

## 3.3 eval()
#eval()對表達式求值,後者可以為字元串或内建函數complie()建立的預編譯代碼對象。這是
#eval()第一個也是最重要的參數。第二個和第三個參數,都為可選的,分别代表了全局和局部名字空間中的對象。
#如果給出這兩個參數,globals 必須是個字典,locals可以是任意的映射對象,比如,一個實作了__getitem__()方法的對象。
#如果都沒給出這兩個參數,分别預設為globals()和locals()傳回的對象,如果隻傳入了一個全局字典,那麼該字典也作為locals 傳入。

#print(eval('932'))
#932
#print(int('932'))
#932
#
#    在這種情況下,eval()和int()都傳回相同的結果:整數932。然而,它們采用的方式卻不盡相
#同。内建函數eval()接收引号内的字元串并把它作為python 表達式進行求值。内建函數int()接收
#代表整數的字元串并把它轉換為整數。這隻有在該字元串隻由字元串932 組成的時候才會成功,而
#該字元串作為表達式傳回值932,932 也是字元串”932”所代表的整數。當我們用純字元串表達式
#的時候,兩者便不再相同了:
#print(eval('100 + 200'))
#300
#print(int('100 + 200'))
#ValueError: invalid literal for int(): 100 + 200


## 3.4 exec

#和eval()相似,exec 語句執行代碼對象或字元串形式的python 代碼。類似地,用compile()
#預編譯重複代碼有助于改善性能,因為在調用時不必經過位元組編譯處理。exec 語句隻接受一個參數,
#下面便是它的通用文法:
#exec obj

#被執行的對象(obj)可以隻是原始的字元串,比如單一語句或是語句組,它們也可以預編譯成
#一個代碼對象(分别用'single'和'exec"參數)

#exec_code = compile("""
#req = input('Count how many numbers? ')
#for eachNum in range(int(req)):
#    print(eachNum)
#""", '', 'exec')
#exec(exec_code)


## 3.5 input() 
#内建函數input()是eval()和raw_input()的組合,等價于eval(raw_input())。類似于
#raw_input(),input()有一個可選的參數,該參數代表了給使用者的字元串提示。如果不給定參數的
#話,該字元串預設為空串。
#從功能上看,input 不同于raw_input(),因為raw_input()總是以字元串的形式,逐字地傳回用
#戶的輸入。input()履行相同的的任務;而且,它還把輸入作為python 表達式進行求值。這意味着
#input()傳回的資料是對輸入表達式求值的結果:一個python 對象。


##************  Part 4: 執行其他(非Python)程式  *****************
#在python 程式裡我們也可以執行非python 程式。這些程式包括了二進制可執行檔案,其他的
#shell 腳本等等。所有的要求隻是一個有效的執行環境,比如,允許檔案通路和執行,腳本檔案必須
#能通路它們的解釋器(perl, bash,等等),二進制必須是可通路的(和本地機器的構架相容)

#執行外部程式的os 子產品函數(u 隻對unix 有效, w 隻對windows 有效)
#os 子產品函數     描述
#system(cmd)     執行程式cmd(字元串),等待程式結束,傳回退出代碼(windows 下,始終為0)
#fork()          建立一個和父程序并行的子程序[通常來說和exec*()一起使用];傳回兩次....一次給父程序一次給子程序
#execl(file, arg0,arg1,...)          用參數清單arg0, arg1 等等執行檔案
#execv(file, arglist)                除了使用參數向量清單,其他的和execl()相同
#execle(file, arg0,arg1,... env)     和execl 相同,但提供了環境變量字典env
#execve(file,arglist, env)           除了帶有參數向量清單,其他的和execle()相同
#execlp(cmd, arg0,arg1,...)          于execl()相同,但是在使用者的搜尋路徑下搜尋完全的檔案路徑名
#execvp(cmd, arglist)                除了帶有參數向量清單,與execlp()相同
#
#execlpe(cmd, arg0, arg1,... env)    和execlp 相同,但提供了環境變量字典env
#execvpe(cmd,arglist, env)           和execvp 相同,但提供了環境變量字典env
#spawn*a(mode, file, args[, env])     spawn*()家族在一個新的程序中執行路徑,args 作為參數,也許還有環境變量的字典env;模式(mode)是
#個顯示不同操作模式的魔術。
#wait()                              等待子程序完成[通常和fock 和exec*()一起使用] ○U
#waitpid(pid,options)                等待指定的子程序完成[通常和fock 和exec*()一起使用] ○U
#popen(cmd, mode='r',buffering=-1)       執行字元串cmd,傳回一個類檔案對象作為運作程式通信句柄,預設為讀取模式和預設系統緩沖
#startfileb(path)                       用關聯的應用程式執行路徑 W

## 4.1 os.system()
#system(),一個非常簡單的函數,接收字元串形式的系統指令并執行它。當執行指令的時候,python 的運作是挂起的。
#當我們的執行完成之後,将會以system()的傳回值形式給出退出狀态,python 的執行也會繼續。

#system()保留了現有的标準檔案,包括标準的輸出,意味着執行任何的指令和程式顯示輸出都
#會傳到标準輸出上。這裡要當心,因為特定應用程式比如公共網關接口(CGI),如果将除了有效的超
#文本标示語言(HTML)字元串之外的輸出,經過标準輸出發送回用戶端,會引起web 浏覽器錯誤。
#system()通常和不會産生輸出的指令一起使用,其中的一些指令包括了壓縮或轉換檔案的程式,挂
#載磁盤到系統的程式,或其他執行特定任務的指令---通過退出狀态顯示成功或失敗而不是通過輸入
#和/或輸出通信。通常的約定是利用退出狀态,0 表示成功,非零表示其他類型的錯誤。

#import os
#result = os.system('uname -a')
#print(result)

## 4.2 os.popen()
#popen()函數是檔案對象和system()函數的結合。它工作方式和system()相同,但它可以建立
#一個指向那個程式的單向連接配接,然後如通路檔案一樣通路這個程式。如果程式要求輸入,那麼你要
#用'w'模式寫入那個指令來調用popen()。你發送給程式的資料會通過标準輸入接收到。同樣地,'r'
#模式允許spawn 指令,那麼當它寫入标準輸出的時候,你就可以通過類檔案句柄使用熟悉的file 對
#象的read*()方法來讀取輸入。就像對于檔案,當使用完畢以後,你應當close()連接配接。

#import os
#f = os.popen('uname -a')
#data = f.readline()
#f.close()
#print(data)


##************  Part 5: 結束執行  *****************
#當程式運作完成,所有子產品最進階的語句執行完畢後退出,我們便稱這是幹淨的執行。可能有
#很多情況,需要從python 提前退出,比如某種緻命錯誤,或是不滿足繼續執行的條件的時候。
#在python 中,有各種應對錯誤的方法。其中之一便是通過異常和異常處理。另外一個方法便是
#建造一個“清掃器”方法,這樣便可以把代碼的主要部分放在if 語句裡,在沒有錯誤的情況下執行,
#因而可以讓錯誤的情況“正常地“終結。然而,有時也需要在退出調用程式的時候,傳回錯誤代碼
#以表明發生何種事件。

## 5.1 sys.exit() and SystemExit
#立即退出程式并傳回調用程式的主要方式是sys 子產品中的exit()函數。sys.exit()的文法為:
#    sys.exit(status=0)
#當調用sys.exit()時,就會引發systemExit()異常。除非對異常進行監控(在一個try 語句和
#合适的except 子句中),異常通常是不會被捕捉到或處理的,解釋器會用給定的狀态參數退出,如
#果沒有給出的話,該參數預設為0。System Exit 是唯一不看作錯誤的異常。它僅僅表示要退出python
#的願望。

#sys.exit()經常用在指令調用的中途發現錯誤之後,比如,如果參數不正确,無效,或者參數數目不正确。

## 5.2 sys.exitfunc()
#sys.exitfunc()預設是不可用的,但你可以改寫它以提供額外的功能。當調用了sys.exit()并
#在解釋器退出之前,就會用到這個函數了。這個函數不帶任何參數的,是以你建立的函數也應該是
#無參的。
#如果sys.exitfunc 已經被先前定義的exit 函數覆寫了,最好的方法是把這段代碼作為你exit()
#函數的一部分來執行。一般說來,exit 函數用于執行某些類型的關閉活動,比如關閉檔案和網絡連
#接,最好用于完成維護任務,比如釋放先前保留的系統資源。


## 5.3 os._exit()和 os._exit() 函數
#os 子產品的_exit()函數不應該在一般應用中使用。(平台相關,隻适用特定的平台,比如基于Unix
#的平台,以及Win32 平台)。其文法為:
#os._exit(status)
#這個函數提供的功能與sys.exit()和sys.exitfunc()相反,根本不執行任何清理便立即退出
#python。與sys.exit()不同,狀态參數是必需的。通過sys.exit()退出是退出解釋器的首選方法。

## 5.4 os.kill()
#os 子產品的kill()函數模拟傳統的unix 函數來發送信号給程序。kill()參數是程序辨別數(PID)
#和你想要發送到程序的信号。發送的典型信号為SIGINT, SIGQUIT,或更徹底地,SIGKILL,來使程序終結。