python 元類程式設計 python的元類程式設計

2021-10-13 10:32:52 字數 4966 閱讀 5449

一、property動態屬性

給類中的乙個方法函式加上@property裝飾器,將這個方法變成屬性描述符,將獲取方法變為獲取屬性。

class user:

def __init__(self, name, birthday):

self.name = name

self.birthday = birthday

@property

def age(self):

return datetime.now().year - self.birthday.year

if __name__ == "__main__":

user = user("tom", date(year=2000, month=1, day=1))

print(user.age)

還可以通過給乙個方法函式加上@方法名.setter裝飾器,將這個方法變成屬性描述符,用給屬性賦值的方式給方法函式傳遞引數。

class user:

def __init__(self, name, birthday):

self.name = name

self.birthday = birthday

self._age = 0

@age.setter

def age(self, value):

self._age = value

if __name__ == "__main__":

user = user("tom", date(year=2000, month=1, day=1))

user.age = 10

print(user.age)

二、__getattr& __getattribute

2.1 __getattr__

查詢不到屬性的時候呼叫此方法。

class user:

def __init__(self, name, birthday, info={}):

self.name = name

self.birthday = birthday

self.info = info

def __getattr__(self, item):

return self.info[item]

if __name__ == "__main__":

user = user("tom", date(year=2000, month=1, day=1), info=)

print(user.school)

sea2.2 __getattribute__

呼叫屬性無論是否存在,都無條件呼叫此方法,把握所有屬性呼叫的介面。

三、屬性描述符

使用@property實現對屬性的控制,在需要對多個屬性進行控制時將會使**冗餘。為了實現對屬性的控制,可以新建乙個類,專門對一類屬性進行控制,這就是屬性描述符。

只要實現__get__、__set__、__delete__三個方法中的任意乙個,就是屬性描述符。資料屬性描述符:__get__、__set__

非資料屬性描述符:__get__

class intfield:

"""屬性描述符"""

def __get__(self, instance, owner):

return self.value

def __set__(self, instance, value):

if not isinstance(value, numbers.intergral):

raise valueerror("an integer is required")

self.value = value

def __delete__(self, instance):

pass

class nondatafield:

def __get__(self, instance, owner):

return self.value

class user:

age = intfield()

if __name__ == "__main__":

user = user()

user.age = 30

四、屬性查詢過程

user = user()

user.age

首先呼叫__getattribute__,如果類定義了__getattr__方法,那麼在__getattribute__丟擲attributeerror的時候就會呼叫到__getattr__,對於__get__描述符的呼叫,則是發生在__getattribute__內部的。

在__getattribute__內部的查詢過程是這樣的:如果age是出現在類或其基類的__dict__中, 且age是資料屬性描述符, 那麼呼叫資料屬性描述符的__get__方法,這有著最高的優先順序。

如果不滿足「類屬性+資料屬性描述符」,就檢視age是否在在例項物件user的__dict__中, 如果有,就直接返回user.__dict__['age']

如果例項物件的__dict__中也沒有,在看age出現在類或其基類的__dict__中的情況如果age是非資料屬性描述符,那麼呼叫其__get__方法

如果不在非資料屬性描述符中,直接返回 __dict__['age']

如果「類屬性+資料屬性描述符」>「例項物件dict」>「類屬性+非資料屬性描述符」>「類屬性dict」都沒有的話,__getattribute__丟擲attributeerror異常如果user有__getattr__方法,呼叫__getattr__方法

如果沒有__getattr__方法,丟擲attributeerror

整個查詢過程簡單來說就是: 「類屬性+資料屬性描述符」>「例項物件__dict__」>「類屬性+非資料屬性描述符」>「類屬性__dict__」>「__getattr__」>「attributeerror異常」

五、__new& __init

__new__是在例項建立之前被呼叫的,建立例項然後返回該例項物件,是個靜態方法。 __init__是當例項物件建立完成後被呼叫的,然後設定物件屬性的一些初始值,通常用在初始化乙個類例項的時候。是乙個例項方法。

class user(object):

def __new__(cls, *args, **kwargs):

print "__new__ called."

return super().__new__(cls)

def __init__(self, name, age):

print "__init__ called."

self.name = name

self.age = age

if __name__ == "__mian__":

user = user(name="tom")

執行過程是這樣的: user = user(name="tom")首先呼叫__new__方法,傳入這個類cls和其他引數,返回user類的乙個例項。

然後呼叫__init__方法,將上一步裡面__new__產生的例項作為self傳入。

六、元類

物件由類建立,類也是物件,則類也需要被類建立,而建立類的類就是元類。 由上面可知我們可以通過過載__new__方法來控制建立類物件,這個過程可以放到元類中執行。

我們首先通過class動態建立乙個類。

def create_class(name):

if name == "user":

class user:

def __str__(self):

return "class user"

return user

elif name == "book":

class book:

def __str__(self):

return "class book"

return book

if __name__ == "__main__":

user = create_class("user")

user = user()

我們也可以通過元類type來動態建立乙個類。

type(object_or_name, bases, dict)object_or_name:類名

bases:繼承的基類

dict:類屬性和方法

# 類方法

def _str(self):

return "_str() called."

# 父類

class baseclass:

def answer(self):

return "baseclass answer() called."

if __name__ == "__main__":

user = type("user", (baseclass, ), )

user = user()

user.name

user._str()

user.answer()

實際上我們通常建立乙個類的時候並不會自己使用type去建立,python會根據建立類的引數中指定的metaclass=metaclass作為元類去建立,如果沒有就會向上尋找父類中的指定元類,如果一直找不到乙個元類metaclass=metaclass,那麼python會自動幫我們用type建立乙個類物件。所以,如果我們想控制生成類物件的乙個過程,就可以自己定義乙個元類,然後在建立類物件的時候指定這個元類。

class metaclass(type):

def __new__(cls, *args, **kwargs):

return super().__new__(cls, *args, **kwargs)

class user(metaclass=metaclass):

def __init__(self, name):

self.name = name

def __str__(self):

return "user."

if __name__ == "__main__":

user = user(name="tom")

user.name

python 元類程式設計

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

python 元類程式設計

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

python元類程式設計

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