python 元類程式設計

2021-10-02 04:48:28 字數 4339 閱讀 7561

__getattr__方法可用來檢查乙個類中是否有乙個屬性,比如:

class

user()

:def

__init__

(self, name)

: self.name = name

def__getattr__

(self, item)

:print

("not find attr"

)def

main()

: user = user(

"dog"

) user.age

if __name__ ==

'__main__'

: main(

)

上面**中user類中是沒有age屬性的,而我們呼叫user.age時也不會報錯,而是執行了__getattr__裡的內容,所以為了反正使用者用了乙個類中沒有的屬性,我們就可以使用__getattr__。

__getattribute__方法也是類似的,不過他執行在__getattr__之前:

class

user()

:def

__init__

(self, name)

: self.name = name

def__getattr__

(self, item)

:print

("not find attr"

)def

__getattribute__

(self, item)

:print

("not find attribute"

)def

main()

: user = user(

"dog"

) user.age

if __name__ ==

'__main__'

: main(

)

如上述**執行後,列印的就不是not find attr,而是not find attribute了。

如果user類中有多個屬性都需要判斷,那麼就需要寫多個方法,這些方法怎麼復用呢?這個時候就要用到屬性描述符

class

intfield

(object):

""" 資料描述符

"""def__get__

(self, instance, owner)

:print

("get"

)return self.values

def__set__

(self, instance, value)

:print

("set")if

notisinstance

(value,

int)

:raise valueerror(

'value error'

) self.values = value

def__delete__

(self, instance)

:pass

class

user

: age = intfield(

)# age = 19

user = user(

)user.age =

30print

(user.age)

屬性描述符,只要實現了__get__,__set__,__delete__任何乙個方法,就被稱為屬性描述符,在上述**執行後,會依次列印出set get 30,,而如果講age = 19 的注釋去除,就不會列印出set get ,只會列印出30.

user = user(), 那麼user.age 順序如下:

1 如果"age"是出現在user或其基類的__dict__中, 且age是data descriptor,那麼呼叫其__get__方法, 否則

2 如果"age"出現在user的__dict__中, 那麼直接返回 obj.dict[『age』],否則

3 如果"age"出現在user或其基類的__dict__中

3.1 如果age是non-data descriptor,那麼呼叫其__get__方法, 否則

3.2 返回dict[『age』]

4 如果user有__getattr__方法,呼叫__getattr__方法,否則

5 丟擲attributeerror

def

create_class

(name)

:if name ==

"user"

:class

user

:def

__str__

(self)

:return

"user"

return user

elif name ==

"student"

:class

student

:def

__str__

(self)

:return

"student"

return student

if __name__ ==

"__main__"

: myclass = create_class(

'user'

) obj = myclass(

)print

(obj)

print

(type

(obj)

)

在上述**中,定義了乙個create_class方法,該方法實現根據引數來建立乙個類的功能,myclass = create_class(『user』)相當於是建立了乙個名字叫myclass的類,而這個類與create_class方法中定義的user類是一樣的。obj = myclass()相當於例項化乙個叫obj的myclass類。最後的列印結果為

user

<

class

'__main__.create_class..user'

>

type除了檢視乙個物件的型別,還能用來建立乙個類,像這種由類建立類的就叫元類:

使用方法為。type(類名,由父類組成的元組,包含屬性的字典)

第乙個引數:name表示類名稱,字串型別

第二個引數:bases表示繼承物件(父類),元組型別,單元素使用逗號

第三個引數:attr表示屬性,這裡可以填寫類屬性、類方式、靜態方法,採用字典格式,key為屬性名,value為屬性值

如果乙個類中定義了metaclass = ***,python就會用元類的方式來建立類

def

upper_attr

(class_name, class_parents, class_attr)

: newattr =

for name, value in class_attr.items():

ifnot name.startswith(

"_")

: newattr[name.upper()]

= value

return

type

(class_name, class_parents, newattr)

class

foo(

object

, metaclass=upper_attr)

:# __metaclass__ = upper_attr

name =

'dog'

f = foo(

)print

(hasattr

(foo,

'name'))

print

(hasattr

(foo,

'name'

))

其中 class_name 對應的是foo,class_parents對應的是object,class_對應的是個字典,字典的鍵為foo裡的屬性,值為屬性對應的值。if not name.startswith("_"):用來過濾掉class_attr中的類似與__module__這種帶_的。

最後列印出來的結果為 false true。

講foo裡的name屬性全大寫為了name。

需注意的是,在python2裡metaclass的用法為**中的注釋部分__metaclass__ = upper_attr,python3中的用法為**中的用法class foo(object, metaclass=upper_attr):。

python 元類程式設計

裝飾器任何時候你定義裝飾器的時候,都應該使用 functools 庫中的 wraps 裝飾器來註解底層包裝函式.因為乙個普通裝飾器作用在某個函式上時,這個函式的重要的元資訊比如名字 文件字串 註解和引數簽名都會丟失。但是 wraps不會。import time from functools impo...

python元類程式設計

當我們希望動態的產生類的時候,就需要用到元類程式設計的手段。要掌握此技術,需要明白以下幾個基本內容 metaclass type new call 在python 中,所有東西都是物件 object 包括python的類 class 也是乙個物件。檢視乙個物件的類,可以用物件的 class 屬性 c...

Python類元程式設計

類元程式設計是指動態地建立或定製類,也就是在執行時根據不同的條件生成符合要求的類,一般來說,類元程式設計的主要方式有類工廠函式,類裝飾器和元類。通常,我們都是使用 class 關鍵字來宣告乙個類,像這樣 class a name a 但是,我們還有另外一種方式來生成類,下述 與上面作用相同 a ty...