1. 程式人生 > 其它 >[python]python動態呼叫模組內的類或方法

[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.