最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

從原理到ORM深入詳解Python屬性描述符使用方法

 更新時(shí)間:2026年03月05日 08:34:40   作者:郝學(xué)勝-神的一滴  
在Python的面向?qū)ο缶幊讨?屬性的控制與查找是核心知識(shí)點(diǎn)之一,而屬性描述符作為實(shí)現(xiàn)屬性精細(xì)化控制的重要工具,更是ORM框架的底層實(shí)現(xiàn)基礎(chǔ),本文將從實(shí)際開發(fā)痛點(diǎn)出發(fā),深入講解屬性描述符的定義、分類、使用方法,需要的朋友可以參考下

在Python的面向?qū)ο缶幊讨?,屬性的控制與查找是核心知識(shí)點(diǎn)之一,而屬性描述符作為實(shí)現(xiàn)屬性精細(xì)化控制的重要工具,更是ORM框架(如Django Model、SQLAlchemy)的底層實(shí)現(xiàn)基礎(chǔ)。本文將從實(shí)際開發(fā)痛點(diǎn)出發(fā),深入講解屬性描述符的定義、分類、使用方法,以及它在Python屬性查找過(guò)程中的核心作用,幫你徹底理解這一重要特性。

一、為什么需要屬性描述符?從property的局限性說(shuō)起

在Python中,我們常用property裝飾器來(lái)控制屬性的獲取、設(shè)置和刪除過(guò)程,實(shí)現(xiàn)對(duì)屬性的簡(jiǎn)單校驗(yàn)和邏輯封裝。比如我們要限制用戶類中age為整數(shù)類型、name為字符串類型,使用property可以輕松實(shí)現(xiàn):

class User:
    @property
    def age(self):
        return self._age
    
    @age.setter
    def age(self, value):
        if not isinstance(value, int):
            raise ValueError("int value need")
        self._age = value

但在實(shí)際開發(fā)中,尤其是在ORM場(chǎng)景下,一個(gè)數(shù)據(jù)模型類往往對(duì)應(yīng)數(shù)據(jù)庫(kù)的一張表,包含十幾個(gè)甚至幾十個(gè)字段,其中很多字段會(huì)有相同的類型校驗(yàn)規(guī)則(如name、email、mobile都是字符串類型,age、id都是整數(shù)類型)。

如果繼續(xù)使用property,我們需要為每個(gè)字段編寫重復(fù)的gettersetter方法,這會(huì)導(dǎo)致代碼冗余度極高,維護(hù)成本大幅增加。為了解決代碼復(fù)用的問(wèn)題,Python為我們提供了更優(yōu)雅的解決方案——屬性描述符。

二、屬性描述符的定義與基礎(chǔ)使用

2.1 什么是屬性描述符?

屬性描述符的定義非常簡(jiǎn)單:一個(gè)自定義類,只要實(shí)現(xiàn)了 __get__ 、 __set__ 、 __delete__ 三個(gè)魔法函數(shù)中的任意一個(gè),這個(gè)類就是屬性描述符。通過(guò)這個(gè)類的實(shí)例,我們可以將屬性的控制邏輯封裝起來(lái),實(shí)現(xiàn)多字段的邏輯復(fù)用。

2.2 基礎(chǔ)實(shí)現(xiàn):整數(shù)類型校驗(yàn)描述符

我們以實(shí)現(xiàn)整數(shù)類型校驗(yàn)為例,編寫第一個(gè)屬性描述符,解決多個(gè)整數(shù)類型字段的校驗(yàn)復(fù)用問(wèn)題:

import numbers

class IntField:
    # 實(shí)現(xiàn)__set__方法,完成類型校驗(yàn)
    def __set__(self, instance, value):
        # 校驗(yàn)是否為整數(shù)類型
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        # 校驗(yàn)是否為正數(shù)
        if value < 0:
            raise ValueError("positive value need")
        # 將值保存到描述符自身實(shí)例,避免死循環(huán)
        self.value = value
    
    # 實(shí)現(xiàn)__get__方法,獲取屬性值
    def __get__(self, instance, owner):
        return self.value

2.3 在模型類中使用描述符

定義好描述符后,我們可以在模型類中直接將其作為類屬性使用,實(shí)現(xiàn)對(duì)字段的統(tǒng)一控制:

class User:
    # 將IntField實(shí)例作為類屬性,實(shí)現(xiàn)age的整數(shù)校驗(yàn)
    age = IntField()

# 測(cè)試正常賦值
user = User()
user.age = 30
print(user.age)  # 輸出:30

# 測(cè)試賦值非整數(shù),拋出異常
user.age = "abc"  # 拋出ValueError: int value need

# 測(cè)試賦值負(fù)數(shù),拋出異常
user.age = -5  # 拋出ValueError: positive value need

2.4 關(guān)鍵注意點(diǎn):避免賦值死循環(huán)

在實(shí)現(xiàn)__set__方法時(shí),切忌將值保存到傳入的 instance (模型類實(shí)例)中,比如寫成instance.age = value。

因?yàn)楫?dāng)我們對(duì)instance.age賦值時(shí),Python會(huì)再次調(diào)用描述符的__set__方法,從而陷入無(wú)限遞歸,最終導(dǎo)致棧溢出。正確的做法是將值保存到描述符自身的實(shí)例self)中,在__get__方法中再?gòu)?code>self中取出。

三、屬性描述符的分類:數(shù)據(jù)描述符與非數(shù)據(jù)描述符

根據(jù)實(shí)現(xiàn)的魔法函數(shù)不同,屬性描述符分為兩類,二者的核心區(qū)別在于在Python屬性查找過(guò)程中的優(yōu)先級(jí)不同,這也是理解屬性查找的關(guān)鍵。

3.1 數(shù)據(jù)描述符(Data Descriptor)

實(shí)現(xiàn)了 __get__ __set__ 方法的描述符稱為數(shù)據(jù)描述符,是我們開發(fā)中最常用的類型,比如上文的IntField就是典型的數(shù)據(jù)描述符。

數(shù)據(jù)描述符擁有最高的屬性查找優(yōu)先級(jí),會(huì)覆蓋實(shí)例自身的屬性值。

3.2 非數(shù)據(jù)描述符(Non-data Descriptor)

只實(shí)現(xiàn)了 __get__ 方法(未實(shí)現(xiàn)__set__)的描述符稱為非數(shù)據(jù)描述符,常見的例子是Python中的函數(shù)(函數(shù)實(shí)現(xiàn)了__get__方法,成為綁定方法)。

非數(shù)據(jù)描述符的優(yōu)先級(jí)低于實(shí)例自身的屬性值,僅在實(shí)例中未找到該屬性時(shí)才會(huì)生效。

四、Python完整的屬性查找過(guò)程:描述符的核心作用

在學(xué)習(xí)屬性描述符之前,我們對(duì)Python屬性查找的認(rèn)知通常是:先查找實(shí)例的 __dict__ ,再查找類的 __dict__ ,最后查找基類的 __dict__

但當(dāng)引入屬性描述符后,Python的屬性查找過(guò)程會(huì)變得更加精細(xì),而這一過(guò)程也是getattr__getattribute__兩個(gè)魔法函數(shù)的底層邏輯。當(dāng)我們使用 實(shí)例.屬性 (如 user.age )的方式訪問(wèn)屬性時(shí),等價(jià)于調(diào)用全局函數(shù) getattr(實(shí)例, 屬性名),其完整的查找順序如下:

4.1 核心查找順序

  1. 調(diào)用 __getattribute__:無(wú)論屬性是否存在,都會(huì)先調(diào)用類中的__getattribute__方法,這是屬性查找的入口;
  2. 檢查數(shù)據(jù)描述符:在實(shí)例的類或基類的__dict__中查找該屬性,若該屬性是數(shù)據(jù)描述符,則直接調(diào)用其__get__方法,返回結(jié)果;
  3. 檢查實(shí)例自身屬性:在實(shí)例的__dict__中查找該屬性,若找到則直接返回值;
  4. 檢查非數(shù)據(jù)描述符/類屬性:在實(shí)例的類或基類的__dict__中查找該屬性:
    1. 若為非數(shù)據(jù)描述符,調(diào)用其__get__方法返回結(jié)果;
    2. 若不是描述符,直接返回類屬性的值;
  5. 調(diào)用 __getattr__:若以上步驟均未找到屬性,會(huì)觸發(fā)AttributeError,此時(shí)若類中定義了__getattr__方法,會(huì)調(diào)用該方法;
  6. 拋出異常:若未定義__getattr__,則直接拋出AttributeError

4.2 關(guān)鍵驗(yàn)證:數(shù)據(jù)描述符覆蓋實(shí)例屬性

數(shù)據(jù)描述符的優(yōu)先級(jí)高于實(shí)例自身屬性,即使我們手動(dòng)給實(shí)例的__dict__添加該屬性,訪問(wèn)時(shí)仍會(huì)優(yōu)先調(diào)用數(shù)據(jù)描述符的__get__方法:

# 延續(xù)上文的IntField和User
user = User()
user.age = 30
# 實(shí)例的__dict__為空,值保存在描述符實(shí)例中
print(user.__dict__)  # 輸出:{}

# 手動(dòng)給實(shí)例__dict__添加age屬性
user.__dict__['age'] = "abc"
# 訪問(wèn)時(shí)仍調(diào)用數(shù)據(jù)描述符,因未給描述符賦值,會(huì)報(bào)錯(cuò)
print(user.age)  # 報(bào)錯(cuò):IntField has no attribute 'value'

4.3 關(guān)鍵驗(yàn)證:非數(shù)據(jù)描述符被實(shí)例屬性覆蓋

若將IntField改為非數(shù)據(jù)描述符(僅實(shí)現(xiàn)__get__),則實(shí)例自身的屬性會(huì)覆蓋描述符:

class NonDataIntField:
    # 僅實(shí)現(xiàn)__get__,非數(shù)據(jù)描述符
    def __get__(self, instance, owner):
        return 10

class User:
    age = NonDataIntField()

user = User()
# 實(shí)例賦值age,覆蓋非數(shù)據(jù)描述符
user.age = 30
print(user.age)  # 輸出:30,而非描述符的10

五、屬性描述符的實(shí)際應(yīng)用:ORM框架的底層基礎(chǔ)

屬性描述符的核心價(jià)值在于屬性邏輯的封裝與復(fù)用,這也是所有Python ORM框架的底層實(shí)現(xiàn)原理。

在Django Model、SQLAlchemy等框架中,我們定義的CharFieldIntegerField、EmailField等字段,本質(zhì)上都是封裝了不同校驗(yàn)規(guī)則和數(shù)據(jù)庫(kù)映射邏輯的屬性描述符

  • 字段的類型校驗(yàn)(如CharField限制字符串)由描述符的__set__方法實(shí)現(xiàn);
  • 字段的數(shù)據(jù)庫(kù)字段映射(如字段長(zhǎng)度、是否為主鍵)由描述符的初始化參數(shù)實(shí)現(xiàn);
  • 字段的取值邏輯由描述符的__get__方法實(shí)現(xiàn)。

通過(guò)屬性描述符,ORM框架將數(shù)據(jù)庫(kù)表的字段與Python類的屬性進(jìn)行了完美映射,讓我們可以用面向?qū)ο蟮姆绞讲僮鲾?shù)據(jù)庫(kù),而無(wú)需編寫重復(fù)的校驗(yàn)和映射代碼。

六、總結(jié)

  1. 屬性描述符的誕生:為解決property在多字段場(chǎng)景下的代碼冗余問(wèn)題,實(shí)現(xiàn)屬性控制邏輯的復(fù)用;
  2. 定義規(guī)則:實(shí)現(xiàn)__get__、__set__、__delete__任一方法的自定義類,即為屬性描述符;
  3. 兩大分類:實(shí)現(xiàn)__get__+__set__數(shù)據(jù)描述符(高優(yōu)先級(jí))、僅實(shí)現(xiàn)__get__非數(shù)據(jù)描述符(低優(yōu)先級(jí));
  4. 屬性查找核心實(shí)例.屬性的查找順序?yàn)椤笖?shù)據(jù)描述符 → 實(shí)例屬性 → 非數(shù)據(jù)描述符/類屬性 → getattr → 異常」;
  5. 實(shí)際價(jià)值:Python ORM框架的底層核心,是實(shí)現(xiàn)屬性精細(xì)化控制和數(shù)據(jù)庫(kù)映射的關(guān)鍵工具。

掌握屬性描述符,不僅能讓我們寫出更優(yōu)雅、更易維護(hù)的Python代碼,更能幫助我們理解主流框架的底層實(shí)現(xiàn)邏輯,提升Python面向?qū)ο缶幊痰暮诵哪芰?。下一篇文章,我們將繼續(xù)深入Python的魔法函數(shù),講解元類編程中__new____init__的核心區(qū)別與使用場(chǎng)景。

以上就是從原理到ORM詳解Python屬性描述符使用方法的詳細(xì)內(nèi)容,更多關(guān)于Python屬性描述符的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python?yield迭代器詳解

    python?yield迭代器詳解

    帶有yield的函數(shù)在Python中被稱之為generator(生成器),也就是說(shuō),當(dāng)你調(diào)用這個(gè)函數(shù)的時(shí)候,函數(shù)內(nèi)部的代碼并不立即執(zhí)行?,這個(gè)函數(shù)只是返回一個(gè)生成器
    2022-11-11
  • 使用pandas庫(kù)讀取和寫入csv文件的實(shí)現(xiàn)示例

    使用pandas庫(kù)讀取和寫入csv文件的實(shí)現(xiàn)示例

    Python提供了多種方式來(lái)讀取和寫入CSV文件,本文主要介紹了使用pandas庫(kù)讀取和寫入csv文件的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-05-05
  • Python+OpenCV實(shí)現(xiàn)在圖像上繪制矩形

    Python+OpenCV實(shí)現(xiàn)在圖像上繪制矩形

    這篇文章主要介紹了如何利用Python和OpenCV實(shí)現(xiàn)在圖像上繪制任意大小的矩形,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下
    2022-03-03
  • 使用Python實(shí)現(xiàn)密碼與驗(yàn)證碼的MD5加密

    使用Python實(shí)現(xiàn)密碼與驗(yàn)證碼的MD5加密

    在現(xiàn)代軟件開發(fā)中,數(shù)據(jù)加密是一個(gè)非常重要的環(huán)節(jié),無(wú)論是用戶密碼、驗(yàn)證碼,還是其他敏感信息,加密都是保護(hù)數(shù)據(jù)安全的關(guān)鍵手段之一,本文將通過(guò)一個(gè)具體的例子,詳細(xì)講解如何使用 Python 實(shí)現(xiàn)密碼與驗(yàn)證碼的 MD5 加密
    2025-02-02
  • Python中tkinter無(wú)法同時(shí)顯示多個(gè)image的解決方法及pack與place解析

    Python中tkinter無(wú)法同時(shí)顯示多個(gè)image的解決方法及pack與place解析

    這篇文章主要介紹了Python中tkinter無(wú)法同時(shí)顯示多個(gè)image的解決方法及pack與place解析,本文給大家分享問(wèn)題原因解析及解決方案,需要的朋友可以參考下
    2023-02-02
  • python 列表推導(dǎo)和生成器表達(dá)式的使用

    python 列表推導(dǎo)和生成器表達(dá)式的使用

    這篇文章主要介紹了python 列表推導(dǎo)和生成器表達(dá)式的使用方法,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2021-02-02
  • python讀取目錄下所有的jpg文件,并顯示第一張圖片的示例

    python讀取目錄下所有的jpg文件,并顯示第一張圖片的示例

    今天小編就為大家分享一篇python讀取目錄下所有的jpg文件,并顯示第一張圖片的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-06-06
  • python3 對(duì)list中每個(gè)元素進(jìn)行處理的方法

    python3 對(duì)list中每個(gè)元素進(jìn)行處理的方法

    今天小編就為大家分享一篇python3 對(duì)list中每個(gè)元素進(jìn)行處理的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • PyQt5實(shí)現(xiàn)QLineEdit正則表達(dá)式輸入驗(yàn)證器

    PyQt5實(shí)現(xiàn)QLineEdit正則表達(dá)式輸入驗(yàn)證器

    這篇文章主要介紹了PyQt5實(shí)現(xiàn)QLineEdit正則表達(dá)式輸入驗(yàn)證器,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Python基于pycrypto實(shí)現(xiàn)的AES加密和解密算法示例

    Python基于pycrypto實(shí)現(xiàn)的AES加密和解密算法示例

    這篇文章主要介紹了Python基于pycrypto實(shí)現(xiàn)的AES加密和解密算法,結(jié)合實(shí)例形式分析了Python使用pycrypto模塊進(jìn)行AES加密與解密操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2018-04-04

最新評(píng)論

塔河县| 县级市| 新和县| 桓台县| 句容市| 织金县| 会同县| 泸水县| 安庆市| 拉萨市| 克什克腾旗| 万源市| 威信县| 华池县| 镇原县| 乳山市| 壤塘县| 宁都县| 织金县| 年辖:市辖区| 盘锦市| 文水县| 治县。| 桐柏县| 诸暨市| 公主岭市| 旬邑县| 攀枝花市| 商河县| 阿克陶县| 增城市| 克山县| 布尔津县| 宜兴市| 鄱阳县| 资兴市| 宣武区| 天门市| 利川市| 怀集县| 嵩明县|