python的描述器descriptor詳解
基本說明
Python的描述器(descriptor)是一種Python對象,可以通過定義一組特定的方法來管理另一個對象的訪問。描述器可以用于控制屬性的讀取、寫入和刪除等操作,同時還可以用于實現(xiàn)計算屬性、類屬性、屬性別名等高級功能。
在Python中,描述器是通過實現(xiàn)__get__()、__set__()和__delete__()方法的對象來定義的。當一個描述器被綁定到一個類的屬性上時,Python會自動將其轉(zhuǎn)化為描述器對象,并在訪問該屬性時調(diào)用對應的描述器方法。
class Descriptor:
def __get__(self, instance, owner):
print("Getting the value")
return self.value
def __set__(self, instance, value):
print("Setting the value")
self.value = value
class MyClass:
attr = Descriptor()
obj = MyClass()
obj.attr = 42
print(obj.attr)描述器是一種強大的Python語言特性,可以用于實現(xiàn)各種高級功能,例如:
- 計算屬性:描述器可以根據(jù)其他屬性的值動態(tài)計算出一個屬性的值,而不是存儲屬性的值。這可以幫助我們簡化代碼,并且可以在不改變接口的情況下改變屬性的實現(xiàn)方式。
- 類屬性:描述器可以讓我們將屬性綁定到類上,而不是綁定到實例上。這可以讓我們在所有實例之間共享屬性值,并且可以在運行時動態(tài)更改屬性的值。
- 屬性別名:描述器可以讓我們定義一個屬性的別名,讓一個屬性具有多個名稱。這可以幫助我們簡化代碼,并且可以在不改變接口的情況下更改屬性的名稱。
- 數(shù)據(jù)驗證:描述器可以讓我們在設(shè)置屬性值之前驗證輸入數(shù)據(jù),確保它們符合我們的預期格式和類型。這可以提高代碼的健壯性,并且可以幫助我們避免一些常見的錯誤。
示例Demo1
假設(shè)我們有一個Temperature類,用于表示溫度。該類有一個名為celsius的屬性,表示攝氏溫度。我們希望實現(xiàn)以下功能:
- 計算屬性
fahrenheit,表示華氏溫度,它應該是一個只讀屬性,可以通過攝氏溫度自動計算得出。 - 限制
celsius屬性的取值范圍在-273.15℃到1000℃之間,如果嘗試設(shè)置超出此范圍的值,應該引發(fā)ValueError異常。
下面是使用描述器實現(xiàn)以上功能的示例代碼:
class Celsius:
def __init__(self, value=0.0):
self._value = value
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if value < -273.15 or value > 1000.0:
raise ValueError("Temperature out of range")
self._value = value
class Temperature:
celsius = Celsius()
@property
def fahrenheit(self):
return self.celsius * 1.8 + 32在這個示例中,我們定義了一個Celsius類,它是一個描述器,用于限制Temperature類的celsius屬性的取值范圍。在Celsius類中,我們實現(xiàn)了__get__()和__set__()方法,分別在讀取和設(shè)置celsius屬性時被調(diào)用。在__set__()方法中,我們檢查輸入的值是否在允許的范圍內(nèi),如果不是,則引發(fā)一個異常。
然后,我們定義了一個Temperature類,它有一個celsius屬性,它被綁定到Celsius類的實例上。我們還定義了一個只讀屬性fahrenheit,它可以通過celsius屬性自動計算得出。
現(xiàn)在,我們可以創(chuàng)建一個Temperature對象,并設(shè)置其celsius屬性,如下所示:
t = Temperature() t.celsius = 25.0 print(t.celsius) # 輸出 25.0 print(t.fahrenheit) # 輸出 77.0
在這個示例中,我們創(chuàng)建了一個Temperature對象,并將其celsius屬性設(shè)置為25.0。然后,我們打印了celsius和fahrenheit屬性的值,它們分別是25.0和77.0,符合我們預期的結(jié)果。
如果我們嘗試設(shè)置一個超出允許范圍的值,例如-300.0,會引發(fā)一個異常,如下所示:
t.celsius = -300.0 # 引發(fā) ValueError: Temperature out of range
示例Demo2
下面是一個示例,演示如何使用描述器將屬性綁定到類上。
假設(shè)我們有一個名為Counter的類,它用于計數(shù)器操作,可以用于記錄創(chuàng)建的對象數(shù)或者其他類級別的計數(shù)。
我們可以使用描述器將計數(shù)器屬性綁定到Counter類上,如下所示:
class Counter:
_count = 0
class CountDescriptor:
def __get__(self, instance, owner):
return owner._count
def __set__(self, instance, value):
owner = type(instance)
owner._count = value
count = CountDescriptor()
def __init__(self):
type(self)._count += 1在這個示例中,我們定義了一個Counter類,它有一個名為count的屬性,它被綁定到了一個內(nèi)部的CountDescriptor描述器類上。
在CountDescriptor類中,我們實現(xiàn)了__get__()和__set__()方法,它們分別在讀取和設(shè)置count屬性時被調(diào)用。在__get__()方法中,我們返回Counter類的_count屬性的值。在__set__()方法中,我們通過獲取實例的類型(即Counter類),并設(shè)置其_count屬性的值來設(shè)置計數(shù)器的值。
然后,在Counter類的__init__()方法中,我們在創(chuàng)建對象時自動增加計數(shù)器的值。
現(xiàn)在,我們可以創(chuàng)建多個Counter對象,并訪問它們的count屬性,如下所示:
c1 = Counter() c2 = Counter() print(c1.count) # 輸出 2 print(c2.count) # 輸出 2 Counter.count = 100 print(c1.count) # 輸出 100 print(c2.count) # 輸出 100
在這個示例中,我們創(chuàng)建了兩個Counter對象,并分別將它們存儲在c1和c2變量中。然后,我們打印它們的count屬性,它們的值都是2,表示我們已經(jīng)創(chuàng)建了兩個Counter對象。
接著,我們將Counter類的count屬性設(shè)置為100,這將改變所有對象的計數(shù)器值。然后,我們再次打印c1和c2的count屬性,它們的值都變成了100,說明我們成功地將屬性綁定到了類上,實現(xiàn)了所有實例之間共享屬性值的效果。
示例Demo3
下面是一個示例,演示如何使用描述器實現(xiàn)屬性別名。
假設(shè)我們有一個Person類,它有一個名為name的屬性,表示人的名字。現(xiàn)在我們想要為name屬性定義一個別名,叫做full_name,以便在某些情況下,我們可以使用full_name屬性來代替name屬性。
我們可以使用描述器來實現(xiàn)這個功能,如下所示:
class NameAlias:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
return getattr(instance, self.name)
def __set__(self, instance, value):
setattr(instance, self.name, value)
class Person:
def __init__(self, name):
self.name = name
full_name = NameAlias("name")在這個示例中,我們定義了一個NameAlias描述器類,它用于實現(xiàn)屬性別名功能。在NameAlias類中,我們實現(xiàn)了__get__()和__set__()方法,它們分別在讀取和設(shè)置full_name屬性時被調(diào)用。在__get__()方法中,我們使用getattr()函數(shù)獲取對象的name屬性值,并返回它。在__set__()方法中,我們使用setattr()函數(shù)設(shè)置對象的name屬性值為傳入的值。
然后,在Person類中,我們定義了一個名為full_name的屬性,它被綁定到了一個NameAlias實例上,用于實現(xiàn)屬性別名。在Person類的__init__()方法中,我們初始化name屬性的值。現(xiàn)在,我們可以創(chuàng)建一個Person對象,并訪問它的name和full_name屬性,如下所示:
p = Person("Alice")
print(p.name) # 輸出 "Alice"
print(p.full_name) # 輸出 "Alice"
p.full_name = "Bob"
print(p.name) # 輸出 "Bob"
print(p.full_name) # 輸出 "Bob"在這個示例中,我們創(chuàng)建了一個Person對象,并將其存儲在p變量中。然后,我們打印它的name和full_name屬性,它們的值都是"Alice",表示它們是等價的。接著,我們將p的full_name屬性設(shè)置為"Bob",這將同時改變name屬性的值。然后,我們再次打印name和full_name屬性,它們的值都是"Bob",說明我們成功地實現(xiàn)了屬性別名的功能。
示例Demo4
下面是一個示例,演示如何使用描述器實現(xiàn)數(shù)據(jù)驗證功能。
假設(shè)我們有一個Person類,它有一個名為age的屬性,表示人的年齡?,F(xiàn)在我們想要對age屬性的輸入數(shù)據(jù)進行驗證,以確保它的值在0到150之間。
我們可以使用描述器來實現(xiàn)這個功能,如下所示:
class AgeValidator:
def __get__(self, instance, owner):
return instance._age
def __set__(self, instance, value):
if not isinstance(value, int) or value < 0 or value > 150:
raise ValueError("Invalid age")
instance._age = value
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
age = AgeValidator()在這個示例中,我們定義了一個AgeValidator描述器類,它用于實現(xiàn)數(shù)據(jù)驗證功能。在AgeValidator類中,我們實現(xiàn)了__get__()和__set__()方法,它們分別在讀取和設(shè)置age屬性時被調(diào)用。在__set__()方法中,我們首先檢查輸入的值是否是整數(shù),并且是否在0到150之間。如果輸入數(shù)據(jù)不符合要求,我們將引發(fā)一個ValueError異常。否則,我們將設(shè)置instance._age屬性的值為傳入的值。
然后,在Person類中,我們定義了一個名為age的屬性,它被綁定到了一個AgeValidator實例上,用于實現(xiàn)數(shù)據(jù)驗證功能。在Person類的__init__()方法中,我們初始化name和age屬性的值?,F(xiàn)在,我們可以創(chuàng)建一個Person對象,并嘗試設(shè)置其age屬性的值,如下所示:
p = Person("Alice", 25)
print(p.age) # 輸出 25
p.age = 200 # 引發(fā) ValueError: Invalid age在這個示例中,我們創(chuàng)建了一個Person對象,并將其存儲在p變量中。然后,我們打印它的age屬性,它的值是25。接著,我們嘗試將p的age屬性設(shè)置為200,這將引發(fā)一個ValueError異常,因為200超出了允許的范圍。這說明我們成功地使用描述器實現(xiàn)了數(shù)據(jù)驗證功能,可以避免一些常見的錯誤。
在這個示例中,Person類中有兩個名為age的定義,一個是在__init__()方法中進行初始化的實例屬性,另一個是通過描述器AgeValidator綁定到Person類上的類屬性。
當我們在__init__()方法中設(shè)置self.age = age時,它實際上是在創(chuàng)建一個實例屬性,而不是類屬性。這個實例屬性只對當前對象有效,而不對所有Person對象都有效。
而當我們在Person類中定義了一個名為age的描述器屬性時,它實際上是在創(chuàng)建一個類屬性,這個屬性可以被所有Person對象所共享,它定義了age屬性的讀寫行為,可以對屬性值進行驗證,保證數(shù)據(jù)的正確性。
因此,這兩個age屬性雖然名字相同,但它們的作用范圍和用途不同。實例屬性是用來存儲每個對象的不同數(shù)據(jù),而類屬性則是用來實現(xiàn)屬性的共享和控制。描述器可以讓我們將類屬性綁定到一個自定義的屬性讀寫行為上,從而實現(xiàn)更靈活的屬性操作。
到此這篇關(guān)于python的描述器descriptor詳解的文章就介紹到這了,更多相關(guān)python的描述器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python網(wǎng)絡安全格式字符串漏洞任意地址覆蓋大數(shù)字詳解
這篇文章主要介紹了Python網(wǎng)絡安全格式字符串漏洞任意地址覆蓋大數(shù)字的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2021-10-10
Python處理列表套字典結(jié)構(gòu)JSON數(shù)據(jù)的三種方式對比
這篇文章主要為大家詳細介紹了Python處理JSON文件中列表套字典結(jié)構(gòu)的三種優(yōu)化方法,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2026-02-02
基于Python實現(xiàn)定時自動給微信好友發(fā)送天氣預報
這篇文章主要介紹了基于Python實現(xiàn)定時自動給微信好友發(fā)送天氣預報的實現(xiàn)代碼,,需要的朋友可以參考下2018-10-10
用TensorFlow實現(xiàn)多類支持向量機的示例代碼
這篇文章主要介紹了用TensorFlow實現(xiàn)多類支持向量機的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04

