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

Python 中的 __set__ 與 __set_name__ 的具體使用

 更新時間:2026年05月15日 08:58:42   作者:無風聽海  
本文主要介紹了Python 中的 __set__ 與 __set_name__ 使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

這兩個方法都屬于描述符協(xié)議(Descriptor Protocol),是 Python 元編程的核心機制之一。

一、描述符協(xié)議基礎(chǔ)

描述符是實現(xiàn)了以下至少一個方法的類:

方法觸發(fā)時機類型
__get__讀取屬性非數(shù)據(jù)描述符(若單獨使用)
__set__設(shè)置屬性數(shù)據(jù)描述符(含此方法)
__delete__刪除屬性數(shù)據(jù)描述符(含此方法)
__set_name__類定義時輔助方法(Python 3.6+)

二、__set__詳解

簽名

def __set__(self, obj, value):
    ...
  • self:描述符實例本身
  • obj:擁有該描述符的類的實例
  • value:被賦予的值

核心機制

class Descriptor:
    def __get__(self, obj, objtype=None):
        print(f"__get__ called, obj={obj}")
        return obj.__dict__.get('_value')

    def __set__(self, obj, value):
        print(f"__set__ called, value={value}")
        obj.__dict__['_value'] = value   # 注意:不能用 obj.attr = value,會無限遞歸!


class MyClass:
    attr = Descriptor()


m = MyClass()
m.attr = 42    # 觸發(fā) __set__
print(m.attr)  # 觸發(fā) __get__

輸出:

__set__ called, value=42
__get__ called, obj=<__main__.MyClass object>
42

?? 數(shù)據(jù)描述符 vs 非數(shù)據(jù)描述符

屬性查找優(yōu)先級(MRO 之后):

數(shù)據(jù)描述符  >  實例 __dict__  >  非數(shù)據(jù)描述符

class NonData:
    """只有 __get__,非數(shù)據(jù)描述符"""
    def __get__(self, obj, objtype=None):
        return "from descriptor"

class Data:
    """有 __set__,數(shù)據(jù)描述符"""
    def __get__(self, obj, objtype=None):
        return "from descriptor"
    def __set__(self, obj, value):
        pass

class A:
    x = NonData()
    y = Data()

a = A()
a.__dict__['x'] = "from instance"
a.__dict__['y'] = "from instance"

print(a.x)  # "from instance"   ← 實例 __dict__ 勝出
print(a.y)  # "from descriptor" ← 數(shù)據(jù)描述符勝出!

實戰(zhàn):類型驗證描述符

class TypedField:
    def __init__(self, expected_type):
        self.expected_type = expected_type
        self.storage_name = None  # 后面由 __set_name__ 填充

    def __set__(self, obj, value):
        if not isinstance(value, self.expected_type):
            raise TypeError(
                f"'{self.storage_name}' 期望 {self.expected_type.__name__},"
                f"得到 {type(value).__name__}"
            )
        obj.__dict__[self.storage_name] = value

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__.get(self.storage_name)


class Person:
    name = TypedField(str)
    age  = TypedField(int)

p = Person()
p.name = "Alice"   # ?
p.age  = 30        # ?
p.age  = "thirty"  # ? TypeError

三、__set_name__詳解(Python 3.6+)

為什么需要它?

__set_name__ 出現(xiàn)之前,描述符不知道自己在宿主類中叫什么名字,只能手動傳入:

# 舊方式(繁瑣且容易出錯)
class Person:
    name = TypedField(str, 'name')  # 必須手動重復(fù)寫名字
    age  = TypedField(int, 'age')

簽名

def __set_name__(self, owner, name):
    ...
  • self:描述符實例
  • owner:擁有該描述符的(不是實例?。?/li>
  • name:該描述符在類中的屬性名

觸發(fā)時機

__set_name__類定義完成時由元類 type.__new__ 自動調(diào)用,早于任何實例的創(chuàng)建。

class Descriptor:
    def __set_name__(self, owner, name):
        print(f"__set_name__ 被調(diào)用: owner={owner.__name__}, name='{name}'")
        self.name = name

class MyClass:
    foo = Descriptor()  # 類定義時立即觸發(fā)
    bar = Descriptor()

# 輸出:
# __set_name__ 被調(diào)用: owner=MyClass, name='foo'
# __set_name__ 被調(diào)用: owner=MyClass, name='bar'

與__set__聯(lián)動:完整的描述符

class Validated:
    """通用驗證描述符,自動感知自身名稱"""

    def __set_name__(self, owner, name):
        self.public_name  = name           # 外部訪問名,如 'age'
        self.private_name = '_' + name     # 內(nèi)部存儲名,如 '_age'

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return getattr(obj, self.private_name, None)

    def __set__(self, obj, value):
        value = self.validate(value)
        setattr(obj, self.private_name, value)

    def validate(self, value):
        raise NotImplementedError


class PositiveInt(Validated):
    def validate(self, value):
        if not isinstance(value, int) or value <= 0:
            raise ValueError(f"必須是正整數(shù),得到: {value!r}")
        return value


class NonEmptyStr(Validated):
    def validate(self, value):
        if not isinstance(value, str) or not value.strip():
            raise ValueError(f"不能為空字符串,得到: {value!r}")
        return value


class Product:
    name  = NonEmptyStr()
    price = PositiveInt()
    stock = PositiveInt()

p = Product()
p.name  = "Python Book"
p.price = 99
p.stock = 10

print(p.name, p.price, p.stock)  # Python Book 99 10
print(p.__dict__)                 # {'_name': 'Python Book', '_price': 99, '_stock': 10}

四、__set_name__的高級用法

1. 感知宿主類(owner)

class LoggedField:
    def __set_name__(self, owner, name):
        self.name = name
        # 在宿主類上注冊所有被追蹤的字段
        if not hasattr(owner, '_tracked_fields'):
            owner._tracked_fields = []
        owner._tracked_fields.append(name)

    def __set__(self, obj, value):
        print(f"[LOG] {type(obj).__name__}.{self.name} = {value!r}")
        obj.__dict__[self.name] = value

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__.get(self.name)


class Config:
    host    = LoggedField()
    port    = LoggedField()
    timeout = LoggedField()

print(Config._tracked_fields)  # ['host', 'port', 'timeout']

c = Config()
c.host = "localhost"   # [LOG] Config.host = 'localhost'
c.port = 8080          # [LOG] Config.port = 8080

2. 動態(tài)添加描述符(手動調(diào)用__set_name__)

如果在類定義之后動態(tài)添加描述符,需要手動調(diào)用 __set_name__

class MyDesc:
    def __set_name__(self, owner, name):
        self.name = name
    def __set__(self, obj, value):
        obj.__dict__[self.name] = value * 2
    def __get__(self, obj, objtype=None):
        return obj.__dict__.get(self.name) if obj else self

# 動態(tài)掛載
desc = MyDesc()
MyClass.new_attr = desc
desc.__set_name__(MyClass, 'new_attr')  # ? 必須手動調(diào)用!

m = MyClass()
m.new_attr = 10
print(m.new_attr)  # 20

五、與property的對比

property 本身就是一個描述符,但描述符更靈活,可以復(fù)用:

# property:每個類屬性都要寫一次
class Circle:
    @property
    def radius(self):
        return self._radius
    @radius.setter
    def radius(self, v):
        if v < 0: raise ValueError
        self._radius = v

# 描述符:一次定義,到處復(fù)用
class PositiveNumber:
    def __set_name__(self, owner, name):
        self.name = name
    def __set__(self, obj, value):
        if value < 0: raise ValueError(f"{self.name} 不能為負")
        obj.__dict__[self.name] = value
    def __get__(self, obj, objtype=None):
        return obj.__dict__.get(self.name) if obj else self

class Circle:
    radius = PositiveNumber()

class Rectangle:
    width  = PositiveNumber()
    height = PositiveNumber()

六、總結(jié)

__set__(self, obj, value)
  └─ 在實例賦值時觸發(fā)(obj.attr = value)
  └─ 使描述符成為"數(shù)據(jù)描述符",優(yōu)先級高于實例 __dict__
  └─ 存儲值時用 obj.__dict__ 或 setattr(obj, private_name, value)

__set_name__(self, owner, name)
  └─ 在類定義完成時由元類自動調(diào)用(一次性)
  └─ 提供描述符在類中的名稱,解決自我引用問題
  └─ 也可感知宿主類,用于注冊、元數(shù)據(jù)收集等
  └─ 動態(tài)掛載時需手動調(diào)用

兩者配合使用是構(gòu)建可復(fù)用、類型安全的屬性系統(tǒng)的標準模式,也是 dataclasses、SQLAlchemy、Django ORM 等框架底層的核心機制。

到此這篇關(guān)于Python 中的 __set__ 與 __set_name__ 的具體使用的文章就介紹到這了,更多相關(guān)Python  __set__ 與 __set_name__ 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • Python使用XlsxWriter庫操作Excel詳解

    Python使用XlsxWriter庫操作Excel詳解

    XlsxWriter 是一個功能強大的 Python 模塊,專門用于生成 Microsoft Excel 2007及以上版本的電子表格文件,本文主要為大家介紹了如何使用XlsxWriter庫進行Excel基本操作,需要的可以參考下
    2023-11-11
  • python讀取目錄下最新的文件夾方法

    python讀取目錄下最新的文件夾方法

    今天小編就為大家分享一篇python讀取目錄下最新的文件夾方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • pymongo實現(xiàn)多結(jié)果進行多列排序的方法

    pymongo實現(xiàn)多結(jié)果進行多列排序的方法

    這篇文章主要介紹了pymongo實現(xiàn)多結(jié)果進行多列排序的方法,涉及Python排序的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • 解決安裝pytorch因網(wǎng)速問題失敗的情況

    解決安裝pytorch因網(wǎng)速問題失敗的情況

    這篇文章主要介紹了解決安裝pytorch因網(wǎng)速問題失敗的情況,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • PyTorch解決ModuleNotFoundError: No module named ‘torch’

    PyTorch解決ModuleNotFoundError: No module named

    本文主要介紹了PyTorch解決ModuleNotFoundError: No module named ‘torch’,這個錯誤意味著我們的Python環(huán)境中沒有安裝PyTorch庫,無法正常使用其功能,下面就來具體介紹一下
    2024-03-03
  • Python入門教程之pycharm安裝/基本操作/快捷鍵

    Python入門教程之pycharm安裝/基本操作/快捷鍵

    這篇文章主要介紹了Python入門教程之pycharm安裝/基本操作/快捷鍵,Python是一門非常強大好用的語言,也有著易上手的特性,本文為入門教程,需要的朋友可以參考下
    2023-04-04
  • python如何求數(shù)組連續(xù)最大和的示例代碼

    python如何求數(shù)組連續(xù)最大和的示例代碼

    這篇文章主要介紹了python如何求數(shù)組連續(xù)最大和的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-02-02
  • python實現(xiàn)小程序推送頁面收錄腳本

    python實現(xiàn)小程序推送頁面收錄腳本

    這篇文章主要介紹了python實現(xiàn)小程序推送頁面收錄腳本,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • python Django框架快速入門教程(后臺管理)

    python Django框架快速入門教程(后臺管理)

    Django 是一個開放源代碼的 Web 應(yīng)用框架,由 Python 寫成,今天通過本文給大家介紹python Django框架快速入門教程,適用后臺管理,感興趣的朋友跟隨小編一起看看吧
    2021-07-07
  • 使用__init__.py將文件夾設(shè)置成Python模塊示例詳解

    使用__init__.py將文件夾設(shè)置成Python模塊示例詳解

    這篇文章主要為大家介紹了使用__init__.py將文件夾設(shè)置成Python模塊示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09

最新評論

关岭| 普兰县| 丰原市| 罗定市| 湟中县| 南投县| 陈巴尔虎旗| 洪洞县| 惠水县| 仁怀市| 新民市| 聂荣县| 肇东市| 珠海市| 临夏县| 绍兴县| 城固县| 靖西县| 沾益县| 开原市| 民县| 北碚区| 苗栗市| 石楼县| 万荣县| 深水埗区| 余干县| 铜山县| 莲花县| 磐石市| 无极县| 潢川县| 扬中市| 常德市| 桦甸市| 浑源县| 安陆市| 剑川县| 桐庐县| 紫金县| 黄陵县|