[python]python動態呼叫模組內的類或方法
需求
寫py程式時候總是碰到下面這種場景,動態生成物件或者函式:
>>> def foo():
print "foo"
>>> def bar():
print "bar"
>>> func_list = ["foo","bar"]
>>> for func in func_list:
func()
TypeError: 'str' object is not callable
這種需要根據字串生成物件或者方法的需求,再java裡大概是反射的一個功能,因為老是用到,所以在這裡總結一下.
一共有以下幾種方式:
eval
>>> for func in func_list:
eval(func)()
foo
bar
eval是最簡單粗暴的方式,會將字串重新解釋為可執行物件,也即是所有的可執行的字串都會被編譯為python物件然後執行結果.這種方式雖然能夠得到正確的結果,但是一旦在某些程式中使用,便可能會給黑客入侵帶來便利,因為可以動態執行所有的字串,那麼關於系統相關資訊的函式也並不例外.eval is evil,除非十分必要,否則應該極力避免.
locals()和globals()
>>> for func in func_list: locals()[func]() foo bar >>> for func in func_list: globals()[func]() foo bar
locals()和globals()分別是儲存python程式變數的兩個dict,每個dict中都存放著不同數目的name[str]和value[object]的條目,其中lacals中儲存的是區域性變數,globas中儲存的是全域性變數.
使用這兩個dict有一定限制,在單檔案中尚可,但如果在多個包,尤其是每個包中有相同名字的物件或方法時,就有點力不從心了.
熟悉程式執行過程的朋友知道,程式在記憶體中分為不同的區,有著不同的空間和生命週期.其空間意義及週期如下:
Local:區域性名稱空間,每個函式所擁有的名稱空間,記錄了函式中定義的所有變數,包括函式的入參、內部定義的區域性變數。
Global:全域性名稱空間,每個模組載入執行時建立的,記錄了模組中定義的變數,包括模組中定義的函式、類、其他匯入的模組、模組級的變數與常量。
Built-in:python自帶的內建名稱空間,任何模組均可以訪問,放著內建的函式和異常。
Local(區域性名稱空間)在函式被呼叫時才被建立,但函式返回結果或丟擲異常時被刪除。(每一個遞迴函式都擁有自己的名稱空間)。
Global(全域性名稱空間)在模組被載入時建立,通常一直保留直到python直譯器退出。
Built-in(內建名稱空間)在python直譯器啟動時建立,一直保留直到直譯器退出。
各名稱空間建立順序:python直譯器啟動 ->建立內建名稱空間 -> 載入模組 -> 建立全域性名稱空間 ->函式被呼叫 ->建立區域性名稱空間
各名稱空間銷燬順序:函式呼叫結束 -> 銷燬函式對應的區域性名稱空間 -> python虛擬機器(直譯器)退出 ->銷燬全域性名稱空間 ->銷燬內建名稱空間
python直譯器載入階段會創建出內建名稱空間、模組的全域性名稱空間,區域性名稱空間是在執行階段函式被呼叫時動態創建出來的,函式呼叫結束動態的銷燬的。
getattr()
>>> class Foo:
def do_foo(self):
...
def do_bar(self):
...
>>> f = getattr(foo_instance, 'do_' + opname)
>>> f()
多用於類屬性方法的獲取,當然包似乎也行.
methodcaller
>>> class Foo:
def do_foo(self):
print 1
def do_bar(self):
print 2
>>> f = Foo()
>>> from operator import methodcaller
>>> methodcaller('do_foo')(f)
有點類似於getattr,不過getattr可以獲取屬性,methodcaller側重於執行method.