在Python中有多種運行外部程序的方法,比如,運行操作系統命令或另外的Python腳本,或執行一個磁盤上的文件,或通過網絡來運行文件。這完全取決於想要干什么。特定的環境包括:
在當前腳本繼續運行
創建和管理子進程
執行外部命令或程序
執行需要輸入的命令
通過網絡來調用命令
執行命令來創建需要處理的輸出
執行其他的Python腳本
執行一系列動態生成的Python語句
導入Python模塊
Python中,內建和外部模塊都可以提供上述各種功能。程序員得根據實現的需要,從這些模塊中選擇合適的處理方法。
可調用對象
許多Python對象都是我們所說的可調用的,即是任何通過函數操作符()來調用的對象。Python有4中可調用對象:函數,方法,類,以及一些類的實例。
1.函數
Python有3中不同類型的函數對象,第一種是內建函數。
內建函數(BIFs)
BIF是用C/CPP寫的,編譯過后放入Python解釋器,然后把它們作為第一(內建)名字空間的一部分加載進系統。這些函數在_builtin_模塊里,並作為__builtins__模塊導入到解釋器中。
可以用dir()列出函數的所有屬性:
>>> 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__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']
從內部機制來看,因為BIFs和內建方法(BIMs)屬於相同類型,所以對BIF或者BIM調用type()的結果是:
>>> type(dir) <type 'builtin_function_or_method'>
用戶定義的函數(UDF)
UDF通常是用Python寫的,定義在模塊的最高級,因此會作為全局名字空間的一部分裝置到系統中。函數也可以在其他函數體內定義,我們可以對多重嵌套作用域中的屬性進行訪問。
從內部機制來看,用戶自定義的函數是“函數”類型的:
>>> def foo():pass >>> type(foo) <type 'function'>
lambda表達式
lambda表達式和用戶自定義函數相比,略有不同。雖然它們也是返回一個函數對象,但是不是用def語句創建的,而是用lambda關鍵字:
因為lambda表達式沒有給命名綁定的代碼提供基礎結構,所以要通過函數式編程接口來調用,或把它們的引用賦值給一個變量,然后就可以直接調用或者再通過函數來調用。變量僅是個別名,並不是函數對象的名字。
通過lambda來創建函數的對象除了沒有命名之外,和UDF有相同的屬性;__name__或者func_name屬性給定位字符串"<lambda>"
>>> lambdaFunc = lambda x: x * 2 >>> lambdaFunc(12) 24 >>> type(lambdaFunc) <type 'function'> >>> lambdaFunc.__name__ '<lambda>'
如果是UDF的名字,則是這樣:
>>> def foo():pass >>> foo.__name__ 'foo'
以上是三種函數對象
2.方法
用戶自定義方法是被定義為類的一部分的函數。許多Python的數據類型,比如列表和字典,也有方法,被稱為內建方法。為了說明所有權的類型,方法通過對象的名字和句點屬性標識符命名。
內建方法(BIMs)
剛剛我們說了BIF和BIM的類似之處。只有內建類型有內建方法。對於內建方法,type()工廠函數給出了和BIF一樣的輸出。
>>> type([].append) <type 'builtin_function_or_method'>
此外BIM和BIF兩者有相同屬性。不同之處在於BIM的__self__屬性指向一個Python對象,BIF指向None。
用戶定義的方法(UDM)
UDM包含在類定義之中,只是擁有標准函數的包裝,僅有定義他們的類可以使用。如果沒有在子類定義中被覆蓋,也可以通過子類實例來調用它們。
3.類
調用類的結果就是創建了實例,也就是實例化。
4.類的實例
Python給類提供了名為__call__的特別方法,該方法允許程序員創建可調用的對象(實例)。默認情況下,__call__()方法是沒有實現的,這意味着大多數情況下實例是不可調用的。然而,如果在類中覆蓋了這個方法,那么這個類的實例就成為可調用的了。調用這樣的實例對象等同於調用__call__()方法。如:foo()和foo.__call__(foo)的效果相同,這里的foo也作為參數出現,因為是對自己的引用,實例將自動成為每次方法調用的第一個參數,如果__call__()有參數,那么foo(arg)就和foo.__call__(foo, arg)一樣。
代碼對象
可調用對象是Python執行環境里最重要的部分,然而這並不是全部。Python語句,賦值,表達式,甚至還有模塊構成了更宏大的場面。這些可執行對象無法像可調用物那樣被調用。這些代碼塊被稱為代碼對象。
每個可調用物的核心都是代碼對象,由語句,賦值,表達式,以及其他可調用物組成。查看一個模塊意味着觀察一個較大的、包含了模塊中所有代碼的對象。然后代碼分成語句,賦值,表達式,以及可調用物。可調用物又可以遞歸分解到下一層,那里有它自己的代碼對象。
一般來說,代碼對象可以作為函數或者方法調用的一部分來執行,也可用exec語句或內建函數eval()來執行。從整體上看,一個Python模塊的代碼對象是構成該模塊的全部代碼。
如果要執行Python代碼,那么該代碼必須先要轉換成字節編譯的代碼(又稱字節碼)。這才是真正的代碼對象。然而,它們不包含任何關於它們執行環境的信息,這便是可調用物存在的原因,它被用來包裝一個代碼對象並提供額外的信息。
UDF有 udf.func_code 屬性就是代碼對象。UDM的udm.im_func也是一個函數對象,他同樣有它自己的udm.im_func.func_code代碼對象。這樣的話,你會發現,函數對象僅是代碼對象的包裝,方法則是給函數對象的包裝。當研究到最底層,便是一個代碼對象。
可執行的對象聲明和內建函數
Python提供了大量的BIF來支持可調用/可執行對象。
1.callable()
callable()是一個布爾函數,確定一個對象是否可以用函數操作符()來調用。如果可調用便返回True,否則便是False。
2.compile()
compile()函數允許程序員在運行時刻迅速生成代碼對象,然后就可以用exec語句或者內建函數eval()來執行這些對象或者他們進行求值。
compile的三個參數都是必需的,第一參數代表了要編譯的Python代碼。第二個參數是字符串,雖然是必需的,但通常被置為空串,該參數代表了存放代碼對象的文件的名字(字符串類型)。compile的通常用法是動態生成字符串形式的Python代碼,然后生成一個代碼對象——代碼顯然沒有存放在任何文件。最后的參數是個字符串,用來表明代碼的類型。有三個可能值:
'eval' 可求值的表達式[和eval()一起使用]
'single' 單一可執行語句[和exec一起使用]
'exec' 可執行與劇組[和exec一起使用]
可求值表達式
>>> eval_code = compile('10-2','','eval') >>> eval(eval_code) 8
單一可執行語句
>>> single_code = compile('print "Hello world"','','single') >>> single_code <code object <module> at 024DC698, file "", line 1> >>> exec single_code Hello world
可執行語句組
>>> exec_code = compile(""" req = input('Count how many numbers?') for eachNum in range(req): print eachNum """,'','exec') >>> exec exec_code Count how many numbers?6 0 1 2 3 4 5
3.eval()
eval()對表達式求值,表達式可以為字符串或內建函數compile()創建的預編譯代碼對象。這個對象是第一個也是最重要的參數。第二個和第三個參數是可選的,分別代表了全局和局部名稱空間中的對象。如果給出了這兩個參數,全局必須是個字典,局部可以是任意的映射對象。如果沒有給出這兩個參數,分別默認為globals()和locals()返回的對象。如果只傳入了一個全局字典,那么該字典也作為局部參數傳入。
這是eval()的一個例子。
>>> eval('123') 123 >>> int('123') 123 >>> eval('123+234') 357 >>> int('123+234') Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> int('123+234') ValueError: invalid literal for int() with base 10: '123+234'
開始,我們傳入'123'給eval()和int()的時候,返回了相同的結果,但是方式是不盡相同的,eval()接受引號內的字符串把它作為Python表達式求值,int()接受代表整數的字符串並把它轉換為整數。而當我們輸入'123+234'時,情況就不一樣了。int()調用就失敗了。可以認為eval()函數對表達式兩端的引號視而不見,將它執行在解釋器上,返回結果。
4.exec
和eval()相似,exec語句執行代碼對象或字符串形式的Python代碼。類似地,用compile()預編譯重復代碼有助於改善性能,因為在調用時不必經過字節編譯處理。exec只接受一個參數,語法是:
exec obj
obj可以是原始的字符串,比如單一語句或語句組,也可以預編譯層一個代碼對象。
>>> exec """ x = 0 print 'x is currently:',x while x < 5: x += 1 print 'incrementing x to:',x """ x is currently: 0 incrementing x to: 1 incrementing x to: 2 incrementing x to: 3 incrementing x to: 4 incrementing x to: 5
exec還可以接受有效的Python文件對象。如果我們用上面的多行代碼創建一個xcount.py的文件,那么也可以這樣執行相同代碼:
>>> f = open('xcount.py') # open the file >>> exec f # execute the file x is currently: 0 incrementing x to: 1 incrementing x to: 2 incrementing x to: 3 incrementing x to: 4 incrementing x to: 5
上面我們調用了文件f,如果在完成后繼續調用它
>>> exec f >>>
調用會失敗。並不是真正的失敗,只是不再做任何事。事實上,exec已從文件中讀取了全部數據且停留在文件末尾(EOF)。當用相同的文件對象對exec進行調用的時候,沒有可執行的代碼了,所以exec什么都不做。
我們可以用tell()方法來告訴我們處於文件的何處,然后用os.path.getsize()來告訴我們腳本由多大。然后就會發現,這兩個數字完全一樣:
>>> f.tell() 116 >>> f.close() >>> from os.path import getsize >>> getsize('xcount.py') 116
如果想在不關閉和重新打開文件的情況下再次運行它,可以用seek()到文件最開頭並再次調用exec。假定我們還沒有調用f.close(),那么:
>>> f.seek(0) >>> exec f x is currently: 0 incrementing x to: 1 incrementing x to: 2 incrementing x to: 3 incrementing x to: 4 incrementing x to: 5 >>> f.close()
5.input()
之前用到的內建函數input()是eval()和raw_input()的組合,等價於eval(raw_input()),input()和raw_input()一樣有一個可選的參數給用戶字符串提示。
input不同於raw_input(),input()返回的數據是對輸入表達式求值的結果,是一個Python對象。
6.使用Python在運行時生成和執行Python代碼
書上提供了兩個例子,這兩個例子在運行時吧Python代碼作為字符串並執行。
第一個例子是loopmake.py腳本。一個簡單迅速和執行循環的計算機輔助軟件工程。提示用戶給出各種參數,生成代碼字符串,並執行它。
dashes = '\n' + '-' * 50 exec_dict = { 'f':''' #for loop for %s in %s: print %s ''', 's':''' # sequence while loop %s = 0 %s = %s while %s < len(%s): print %s[%s] %s = %s + 1 ''', 'n':''' # counting while loop %s = %d while %s < %d: print %s %s = %s + %d ''' } def main(): ltype = raw_input('Loop type? (For/While)') dtype = raw_input('Data type? (Number/Sequence)') if dtype == 'n': start = input('Starting value? ') stop = input('Ending value (non-inclusive)? ') step = input('Stepping value? ') seq = str(range(start, stop, step)) else: seq = raw_input('Enter sequence:') var = raw_input('Iterative variable name?') if ltype == 'f': exec_str = exec_dict['f'] % (var, seq, var) elif ltype == 'w': if dtype == 's': svar = raw_input('Enter sequence name? ') exec_str = exec_dict['s'] % \ (var, svar, seq, var, svar, svar, var, var, var) elif dtype == 'n': exec_str = exec_dict['n'] % \ (var, start, var, stop, var, var, var, step) print dashes print 'The custom-generated code for you is:' + dashes print exec_str + dashes print 'The execution of the code:' + dashes exec exec_str print dashes if __name__ == '__main__': main()
有興趣的人可以執行一下這段代碼,十分有趣,可以幫助你生成代碼並執行。反正我寫這段代碼的時候感覺到了exec和input的強大。
第二個例子是有條件地執行代碼
這是代碼:
def foo(): return True def bar(): 'bar() does not do much' return True foo.__doc__ = 'foo() does not do much' foo.tester = ''' if foo(): print 'PASSED' else: print 'FAILED' ''' for eachAttr in dir(): obj = eval(eachAttr) if isinstance(obj, type(foo)): if hasattr(obj, '__doc__'): print '\nFunction "%s" has a doc string:\n\t%s'\ % (eachAttr, obj.__doc__) if hasattr(obj, 'tester'): print 'Function "%s" has a tester... executing'\ % eachAttr exec obj.tester else: print 'Function "%s" has no tester... skipping'\ % eachAttr else: print '"%s" is not a function' % eachAttr
下面是執行后的結果:
>>> "__builtins__" is not a function "__doc__" is not a function "__file__" is not a function "__name__" is not a function "__package__" is not a function Function "bar" has a doc string: bar() does not do much Function "bar" has no tester... skipping Function "foo" has a doc string: foo() does not do much Function "foo" has a tester... executing PASSED
代碼並不難理解,但其所做的事的確很有趣不是么?