藉助於python的動態語言特性,很容易對物件進行新增方法或者屬性,這也是python的靈活之一。
動態生成類的屬性及其方法
在某些情況可能要根據不同的引數來動態生成不同的例項方法、靜態方法、類方法。下面的例子中則展示瞭如何動態地向類中新增屬性和方法。
import types
class Person():
def __init__(self,name):
self.name = name
li = Person('Lily')
li.age = 20 # 例項屬性新增,僅對當下例項有效
tom = Person('Tom')
print(tom.age) # 'Person' object has no attribute 'age'
Person.age = None # 類屬性新增
print(tom.age) # None
def eat(self):
print('%s正在吃東西。。'%self.name)
li.eat = types.MethodType(eat, li) # 實際上python所有類都是type類的例項物件,動態新增了Person的例項方法
li.eat()
@staticmethod
def test():
print('這是一個靜態方法。')
Person.test = test # 動態新增動態方法
Person.test()
@classmethod
def test(cls):
print(cls.age) # None,訪問動態建立的age
print('這是一個類方法。')
Person.test = test # 動態新增類方法
Person.test()
class test(object):
__slots__ = ('name','age') # 使用slots來將屬性固定,不允許增刪
動態地建立類
由於所有類都是type類的物件,所以也可以透過type動態地建立類。
Test = type('Test',(object,),{'num':0}) # 所有類都是type的物件,param1為類名,param2為繼承物件,num為類屬性,方法
class Test(object): # 與上述程式碼等效
num = 0
如果需要新增屬性方法,則在相應的傳參字典中新增對應的方法,例如:
Test = type('Test',(object,),{'num':0, 'foo': fun})
動態訪問類中的屬性方法
動態地方法類中的屬性方法,也是一種反射機制。python中的反射/自省的實現,是透過hasattr、getattr、setattr、delattr四個內建函式實現的,其實這四個內建函式不只可以用在類和物件中,也可以用在模組等其他地方,只是在類和物件中用的很多,所以單獨提出來進行解釋。
- hasattr(key) # 返回的是一個bool值,判斷某個成員或者屬性在不在類或者物件中
- getattr(key,default=xxx) # 獲取類或者物件的成員或屬性,如果不存在,則會丟擲AttributeError異常,如果定義了default那麼當沒有屬性的時候會返回預設值。
- setattr(key,value)假如有這個屬性,那麼更新這個屬性,如果沒有就新增這個屬性並賦值value
- delattr(key)刪除某個屬性
其用法如下所示:
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
def show(self):
return self.name,self.age
#學習中遇到問題沒人解答?小編建立了一個Python學習交流群:531509025
obj=Foo("Tom",18)
print(getattr(obj,"name")) # Tom
setattr(obj,"k1",eat)
print(obj.k1) # <function eat at 0x00000162CAD661F0>
print(hasattr(obj,"k1")) # True
delattr(obj,"k1")
show_fun=getattr(obj,"show")
print(show_fun()) # ('Tom', 18)
動態訪問普通全域性函式
有時候需要透過函式名來動態訪問全域性的函式,那麼依然有三種方法。
透過eval,不同由於考慮到安全因素,不能直接這樣去寫,可能會得到惡意程式碼
eval("print")("hello,world!")
透過建立字典,事先將需要呼叫的函式全部放入字典,缺點是每增加一個動態函式,就要更改字典:
dynamic_fun = {"print": print }
dynamic_fun["print"]("hello,world!")
透過呼叫global()來使用
global()維護了一個全域性的變數列表,其實現過程和方式2類似,具體使用時透過global().get(fun_name)
來完成。
類中其他內建屬性方法(魔術方法)
__init__ # 構造初始化函式,__new__之後執行
__new__ # 建立例項所需的屬性,類似於構造方法
__call__ # 可以使得類的例項透過function方式訪問
__class__ # 例項所在的類,例項.__class__
__str__ # 例項的字串表示,可讀性高
__repr__ # 例項的字串表示,準確性高
__del__ # 刪除例項引用,類似於析構方法
__dict__ # 實力自定義屬性,vars(例項.__dict__)
__doc__ # 類文件,help(類或者例項)
__bases__ #當前類的所有父類
__getattribute__ #屬性訪問攔截器