需求
寫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.
