Python - 描述符



Python 描述符

Python 描述符 是一種自定義物件屬性的訪問、賦值和刪除的方式。它們提供了一種強大的機制,透過定義獲取、設定和刪除其值的方法來管理屬性的行為。描述符通常用於實現屬性、方法和屬性驗證。

描述符 是任何實現了至少一個方法(例如 __get__、__set__ 和 __delete__)的物件。這些方法控制如何訪問和修改屬性的值。

Python 描述符如何工作?

當在例項上訪問屬性時,Python 會在例項的類中查詢該屬性。如果找到該屬性並且它是一個描述符,則 Python 會呼叫相應的描述符方法,而不是簡單地返回屬性的值。這允許描述符控制在屬性訪問期間發生的事情。

描述符協議是一種低階機制,Python 中許多高階功能都使用它,例如屬性、方法、靜態方法和類方法。描述符可用於實現延遲載入、型別檢查和計算屬性等模式。

描述符方法

Python 描述符 包括三個主要方法,即 __get__()、__set__() 和 __delete__()。正如我們上面已經討論過的,這些方法分別控制屬性訪問、賦值和刪除的行為。

1. __get__() 方法

描述符中的 __get__() 方法 是 Python 中描述符協議的關鍵部分。它被呼叫以從例項或類中檢索屬性的值。瞭解 __get__() 方法 的工作原理對於建立可以以複雜方式管理屬性訪問的自定義描述符至關重要。

語法

以下是 Python 描述符 __get__ 方法 的語法:

def __get__(self, instance, owner):
   """
   instance: the instance that the attribute is accessed through, or None when accessed through the owner class.
   owner: the owner class where the descriptor is defined.
   """

引數

以下是此方法的引數:

  • self: 描述符例項。
  • instance: 訪問屬性的類的例項。當透過類而不是例項訪問屬性時,它為 None。
  • owner: 擁有描述符的類。

示例

以下是 __get__() 方法的基本示例,其中在訪問 obj.attr 時返回儲存的值 _value

class Descriptor:
   def __get__(self, instance, owner):
      if instance is None:return self
      return instance._value

class MyClass:
   attr = Descriptor()

   def __init__(self, value):
      self._value = value

obj = MyClass(42)
print(obj.attr)  

輸出

42

2. __set__() 方法

__set__() 方法 是 Python 中描述符協議的一部分,用於控制設定屬性值的行為。當由描述符管理的屬性被賦予新值時,__set__() 方法 被呼叫,允許使用者自定義或強制執行賦值規則。

語法

以下是 Python 描述符 __set__() 方法 的語法:

def __set__(self, instance, value):
    """
    instance: the instance of the class where the attribute is being set.
    value: the value to assign to the attribute.
    """

引數

以下是此方法的引數:

  • self: 描述符例項。
  • instance: 設定屬性的類的例項。
  • value: 賦給屬性的值。

示例

以下是 __set__() 方法的基本示例,其中確保賦給 attr 的值是整數:

class Descriptor:
    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise TypeError("Value must be an integer")
        instance._value = value

class MyClass:
    attr = Descriptor()

    def __init__(self, value):
        self.attr = value

obj = MyClass(42)
print(obj.attr)  
obj.attr = 100
print(obj.attr)  

輸出

<__main__.Descriptor object at 0x000001E5423ED3D0>
<__main__.Descriptor object at 0x000001E5423ED3D0>

3. __delete__() 方法

描述符協議中的 __delete__() 方法 允許我們控制從例項中刪除屬性時發生的事情。當刪除屬性時,這對於管理資源、清理或強制執行約束很有用。

語法

以下是 Python 描述符 __delete__() 方法 的語法:

def __delete__(self, instance):
    """
    instance: the instance of the class from which the attribute is being deleted.
    """

引數

以下是此方法的引數:

  • self: 描述符例項。
  • instance: 刪除屬性的類的例項。

示例

以下是 __set__() 方法的基本示例,其中確保賦給 attr 的值是整數:

class LoggedDescriptor:
   def __init__(self, name):
      self.name = name

   def __get__(self, instance, owner):
      return instance.__dict__.get(self.name)

   def __set__(self, instance, value):
      instance.__dict__[self.name] = value

   def __delete__(self, instance):
      if self.name in instance.__dict__:
         print(f"Deleting {self.name} from {instance}")
         del instance.__dict__[self.name]
      else:
         raise AttributeError(f"{self.name} not found")

class Person:
   name = LoggedDescriptor("name")
   age = LoggedDescriptor("age")

   def __init__(self, name, age):
      self.name = name
      self.age = age

# Example usage
p = Person("Tutorialspoint", 30)
print(p.name)  
print(p.age)   

del p.name    
print(p.name) 

del p.age    
print(p.age)   

輸出

Tutorialspoint
30
Deleting name from <__main__.Person object at 0x0000021A1A67E2D0>
None
Deleting age from <__main__.Person object at 0x0000021A1A67E2D0>
None

Python 描述符的型別

在 Python 中,描述符 可以根據它們實現的方法大致分為兩種型別。它們是:

  • 資料描述符
  • 非資料描述符

讓我們詳細瞭解這兩種 Python 描述符,以便我們更好地理解。

1. 資料描述符

資料描述符 是一種 Python 描述符,它定義了 __get__()__set__() 方法。這些描述符優先於例項屬性,這意味著即使存在同名的例項屬性,也會始終呼叫描述符的 __get__()__set__() 方法。

示例

下面是一個數據描述符的示例,它確保屬性始終為整數,並記錄訪問和修改操作。

class Integer:
   def __get__(self, instance, owner):
      print("Getting value")
      return instance._value

   def __set__(self, instance, value):
      print("Setting value")
      if not isinstance(value, int):
         raise TypeError("Value must be an integer")
      instance._value = value

   def __delete__(self, instance):
      print("Deleting value")
      del instance._value

class MyClass:
   attr = Integer()

# Usage
obj = MyClass()
obj.attr = 42  
print(obj.attr)  
obj.attr = 100  
print(obj.attr)  
del obj.attr   

輸出

Setting value
Getting value
42
Setting value
Getting value
100
Deleting value

2. 非資料描述符

非資料描述符是 Python 中的一種描述符型別,它僅定義了__get__()方法。與資料描述符不同,非資料描述符可以被例項屬性覆蓋。這意味著如果存在同名的例項屬性,則它將優先於非資料描述符。

示例

以下是非資料描述符的一個示例,如果例項上未設定屬性,則它提供一個預設值。

class Default:
   def __init__(self, default):
      self.default = default

   def __get__(self, instance, owner):
      return getattr(instance, '_value', self.default)

class MyClass:
   attr = Default("default_value")

# Usage
obj = MyClass()
print(obj.attr)  
obj._value = "Tutorialspoint"
print(obj.attr) 

輸出

default_value
Tutorialspoint

資料描述符與非資料描述符

瞭解 Python 描述符中資料描述符非資料描述符之間的區別對於有效地利用它們的功能至關重要。

標準 資料描述符 非資料描述符
定義 實現 __get__()、__set__() 方法,以及可選的 __delete__() 方法。 僅實現 __get__() 方法。
方法 __get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)(可選)
__get__(self, instance, owner)
優先順序 優先於例項屬性。 被例項屬性覆蓋。
用例 屬性驗證和強制執行,
託管屬性(例如,屬性),
記錄屬性訪問和修改,
強制執行只讀屬性。
方法繫結,
快取和,
提供預設值。

最後,我們可以說 Python 中的描述符提供了一種強大的機制來管理屬性的訪問和修改。瞭解資料描述符和非資料描述符之間的區別以及它們的適當用例對於建立健壯且可維護的 Python 程式碼至關重要。

透過利用描述符協議,開發人員可以實現高階行為,例如型別檢查、快取和只讀屬性。

廣告