一文搞懂Python對(duì)象的描述器機(jī)制
一、什么是Python描述器?
在Python中,“描述器”是一種能夠自定義對(duì)象屬性訪問(wèn)(獲取、設(shè)置、刪除)行為的協(xié)議。它是實(shí)現(xiàn)@property、類屬性、ORM字段、類型檢查的底層支撐。
一句話解釋:只要一個(gè)對(duì)象定義了__get__、__set__或__delete__等特殊方法,并作為另一個(gè)類的類屬性,這個(gè)對(duì)象就成了描述器!
默認(rèn)對(duì)屬性的訪問(wèn)控制是從對(duì)象的字典里面 (__dict__) 中獲取 (get) , 設(shè)置 (set) 和刪除 (delete) 。
舉例來(lái)說(shuō), a.x 的查找順序是, a.__dict__['x'] , 然后 type(a).__dict__['x'] , 然后找 type(a) 的父類 ( 不包括元類 (metaclass) ).如果查找到的值是一個(gè)描述器, Python 就會(huì)調(diào)用描述器的方法來(lái)重寫默認(rèn)的控制行為。
二、描述器協(xié)議:三大魔法方法
| 方法 | 作用 | 用法場(chǎng)景 |
|---|---|---|
| __get__ | 獲取屬性時(shí)調(diào)用 | 動(dòng)態(tài)計(jì)算、懶加載 |
| __set__ | 設(shè)置屬性時(shí)調(diào)用 | 數(shù)據(jù)驗(yàn)證、只讀限制 |
| __delete__ | 刪除屬性時(shí)調(diào)用 | 管控屬性生命周期 |
最簡(jiǎn)單的描述器雛形:
class DemoDescriptor:
def __get__(self, instance, owner):
print('執(zhí)行了__get__')
return 42
class MyClass:
x = DemoDescriptor()
obj = MyClass()
print(obj.x) # 輸出42,中間會(huì)打印“執(zhí)行了__get__”
三、入門實(shí)戰(zhàn):搞懂描述器工作的方式
3.1 數(shù)據(jù)描述器 VS 非數(shù)據(jù)描述器
- 數(shù)據(jù)描述器 : 同時(shí)定義了
__get__和__set__ - 非數(shù)據(jù)描述器 : 只定義了
__get__
數(shù)據(jù)描述器擁有更高優(yōu)先級(jí),會(huì)屏蔽實(shí)例字典上的同名屬性!
3.2 數(shù)據(jù)描述器基礎(chǔ)案例
class Typed:
def __init__(self, name, typ):
self.name = name
self.typ = typ
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.typ):
raise TypeError(f'屬性{self.name}必須是{self.typ}')
instance.__dict__[self.name] = value
class Person:
age = Typed('age', int) # 用描述器控制
name = Typed('name', str)
p = Person()
p.age = 18 # 自動(dòng)類型檢查
p.name = '小明'
# p.age = 'not int' # 拋出TypeError
四、@property 與描述器的關(guān)系
你用過(guò)的@property,其實(shí)就是“非數(shù)據(jù)描述器”的語(yǔ)法糖:
class Demo:
@property
def val(self):
return 666
底層實(shí)現(xiàn)和你定義帶__get__的對(duì)象如出一轍。
五、進(jìn)階:自定義描述器封裝與通用實(shí)現(xiàn)
5.1 描述器工廠 基于裝飾器快速生成校驗(yàn)字段
def typed_field(name, typ):
class _TypedDescriptor:
def __get__(self, instance, owner):
return instance.__dict__[name]
def __set__(self, instance, value):
if not isinstance(value, typ):
raise TypeError(f"屬性{name}必須是{typ}")
instance.__dict__[name] = value
return _TypedDescriptor()
class Student:
age = typed_field('age', int)
name = typed_field('name', str)
stu = Student()
stu.age = 18 # √
# stu.age = 'oops' # TypeError
5.2 只讀屬性描述器
class ReadOnly:
def __init__(self, value):
self._value = value
def __get__(self, instance, owner):
return self._value
class System:
version = ReadOnly('1.0.0')
print(System.version) # '1.0.0'
到此這篇關(guān)于一文搞懂Python對(duì)象的描述器機(jī)制的文章就介紹到這了,更多相關(guān)Python對(duì)象描述器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python求解任意閉區(qū)間的所有素?cái)?shù)
本篇文章是一篇關(guān)于python求素?cái)?shù)的知識(shí)點(diǎn)內(nèi)容,如果大家在應(yīng)用中有用的到,不妨跟著學(xué)習(xí)下。2018-06-06
如何在Python3中使用telnetlib模塊連接網(wǎng)絡(luò)設(shè)備
這篇文章主要介紹了如何在Python3中使用telnetlib模塊連接網(wǎng)絡(luò)設(shè)備,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Pycharm及python安裝詳細(xì)步驟及PyCharm配置整理(推薦)
這篇文章主要介紹了Pycharm及python安裝詳細(xì)步驟以及PyCharm配置整理,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Python操作Excel神器openpyxl使用教程(超詳細(xì)!)
openpyxl庫(kù)是一個(gè)很好處理xlsx的python庫(kù),下面這篇文章主要給大家介紹了關(guān)于Python辦公自動(dòng)化openpyxl使用的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01
解決python文件字符串轉(zhuǎn)列表時(shí)遇到空行的問(wèn)題
下面小編就為大家?guī)?lái)一篇解決python文件字符串轉(zhuǎn)列表時(shí)遇到空行的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07
Python+Yolov5人臉口罩識(shí)別的詳細(xì)步驟
人臉口罩佩戴檢測(cè)(識(shí)別)是當(dāng)前急需的應(yīng)用,而YOLOv5是目前流行的強(qiáng)悍的目標(biāo)檢測(cè)技術(shù),下面這篇文章主要給大家介紹了關(guān)于Python+Yolov5人臉口罩識(shí)別的相關(guān)資料,需要的朋友可以參考下2022-12-12

