Python基本語法之描述符
描述符定義
描述符是一種類,我們把實現了__get__()、__set__()和__delete__()中的其中任意一種方法的類稱之為描述符。
描述符的作用是用來代理一個類的屬性,需要注意的是描述符不能定義在被使用類的建構函式中,只能定義為類的屬性,它只屬於類的,不屬於例項,我們可以通過檢視例項和類的字典來確認這一點。
描述符是實現大部分Python類特性中最底層的資料結構的實現手段,我們常使用的@classmethod、@staticmethd、@property、甚至是__slots__等屬性都是通過描述符來實現的。它是很多高階庫和框架的重要工具之一,是使用到裝飾器或者元類的大型框架中的一個非常重要元件。注:裝飾器和元類等概念我們在以後文章中說明。
如下示例一個描述符及引用描述符類的程式碼:
class Descriptors: def __init__(self, key, value_type): self.key = key self.value_type = value_type def __get__(self, instance, owner): print("執行Descriptors的get") return instance.__dict__[self.key] def __set__(self, instance, value): print("執行Descriptors的set") if not isinstance(value, self.value_type): raise TypeError("引數%s必須為%s"%(self.key, self.value_type)) instance.__dict__[self.key] = value def __delete__(self, instance): print("執行Descriptors的delete") instance.__dict__.pop(self.key) class Person: name = Descriptors("name", str) age = Descriptors("age", int) def __init__(self, name, age): self.name = name self.age = age person = Person("xiaoming", 15) print(person.__dict__) person.name person.name = "jone" print(person.__dict__)
其中,Descriptors類就是一個描述符,Person是使用描述符的類。
類的__dict__屬性是類的一個內建屬性,類的靜態函式、類函式、普通函式、全域性變數以及一些內建的屬性都是放在類__dict__裡。
在輸出描述符的變數時,會呼叫描述符中的__get__方法,在設定描述符變數時,會呼叫描述符中的__set__方法。
如上例子的執行結果如下:
描述符的種類和優先順序
描述符分為資料描述符和非資料描述符。
至少實現了內建__set__()和__get__()方法的描述符稱為資料描述符;實現了除__set__()以外的方法的描述符稱為非資料描述符。
描述符的優先順序的高低順序:類屬性 > 資料描述符 > 例項屬性 > 非資料描述符 > 找不到的屬性觸發__getattr__()。
在上述“描述符定義”章節的例子中,例項person的屬性優先順序低於資料描述符Descriptors,所以在賦值或獲取值過程中,均呼叫了描述符的方法。
如下例子描述了類屬性高於資料描述符:
class Descriptors:
def __get__(self, instance, owner):
print("執行Descriptors的get")
def __set__(self, instance, value):
print("執行Descriptors的set")
def __delete__(self, instance):
print("執行Descriptors的delete")
class University:
name = Descriptors()
def __init__(self, name):
self.name = name
University.name
University.name = "深圳大學"
print(University.name)
my_university = University("廈門大學")
my_university.name
print(my_university.name)
在該例子中,University類的變數被賦值,由於該優先順序最高,所以使用的一直為University類的變數,示例執行結果如下:
如果在這個例子中,刪除University.name = "深圳大學"這一行,則列印是怎麼樣的?
為什麼物件的name屬性沒有值了呢,因為物件的的優先順序沒有描述符高,所以使用描述符的值,而描述符沒有賦值,所以為None,所以雖然物件的屬性有值,仍然無法正常獲取。
描述符相關的優先順序,理解難度稍微大一點,請各位看官仔細品味,大家可以自己寫小例子嘗試列印結果,可以讓您更快的理解該原理。
如果您喜歡這篇文章,別忘了點贊和評論哦!
十年一夢
一覺醒來思未變,
未知恍然已十年。
啜啜小兒成才俊,
市井矗樓連成片。