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

深入理解Python __init_subclass__的使用

 更新時(shí)間:2026年05月15日 09:07:13   作者:無風(fēng)聽海  
本文主要介紹了Python __init_subclass__的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、從一個(gè)問題出發(fā)

當(dāng)你定義一個(gè)基類,希望所有子類在被定義時(shí)(而非實(shí)例化時(shí))就自動(dòng)完成某些注冊(cè)、校驗(yàn)或增強(qiáng)邏輯,你會(huì)怎么做?

傳統(tǒng)方案是元類(metaclass),但元類的心智負(fù)擔(dān)極重。Python 3.6 引入的 __init_subclass__ 正是為了解決這一痛點(diǎn)——用一個(gè)普通的類方法,優(yōu)雅地?cái)r截子類的創(chuàng)建過程。

二、它是什么

class Base:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        # cls 是正在被定義的子類,不是 Base 本身

__init_subclass__ 是一個(gè)隱式的 classmethod,定義在父類中,每當(dāng)有子類繼承該父類時(shí),Python 解釋器會(huì)自動(dòng)調(diào)用它,并將新創(chuàng)建的子類作為第一個(gè)參數(shù) cls 傳入。

PEP 487(Python 3.6)正式引入此機(jī)制,目標(biāo)是提供一種比元類更輕量的子類定制鉤子。

三、調(diào)用時(shí)機(jī)與調(diào)用鏈

class A:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        print(f"A.__init_subclass__ called: {cls}")

class B(A):          # 觸發(fā) A.__init_subclass__(B)
    pass

class C(B):          # 觸發(fā) A.__init_subclass__(C)(沿 MRO 向上找)
    pass

輸出:

A.__init_subclass__ called: <class '__main__.B'>
A.__init_subclass__ called: <class '__main__.C'>

關(guān)鍵細(xì)節(jié):

時(shí)機(jī)是否觸發(fā)
定義 Base 本身? 不觸發(fā)
直接繼承 Base 的子類被定義? 觸發(fā)
子類的子類被定義? 觸發(fā)(沿 MRO 傳播)
實(shí)例化子類? 不觸發(fā)

四、傳遞關(guān)鍵字參數(shù)

__init_subclass__ 最精妙的設(shè)計(jì)之一是支持通過 class 語句的關(guān)鍵字參數(shù)向父類傳遞配置:

class Animal:
    def __init_subclass__(cls, sound: str = "...", **kwargs):
        super().__init_subclass__(**kwargs)
        cls.sound = sound
        print(f"Registered {cls.__name__} with sound '{sound}'")
class Dog(Animal, sound="woof"):
    pass
class Cat(Animal, sound="meow"):
    pass
print(Dog.sound)  # woof
print(Cat.sound)  # meow

這些關(guān)鍵字參數(shù)不會(huì)出現(xiàn)在 __init__ 中,它們專屬于類定義階段,語義清晰,無副作用。

?? 務(wù)必用 **kwargs 接收未消費(fèi)的參數(shù)并傳給 super(),否則多重繼承時(shí)會(huì)因參數(shù)不匹配而拋出 TypeError。

五、核心應(yīng)用場景

5.1 自動(dòng)注冊(cè)子類(插件系統(tǒng))

這是最經(jīng)典的用法,無需手動(dòng)維護(hù)注冊(cè)表:

class Handler:
    _registry: dict[str, type] = {}

    def __init_subclass__(cls, name: str | None = None, **kwargs):
        super().__init_subclass__(**kwargs)
        key = name or cls.__name__.lower()
        Handler._registry[key] = cls
        print(f"Handler '{key}' registered.")

class JSONHandler(Handler, name="json"):
    def handle(self): ...

class XMLHandler(Handler, name="xml"):
    def handle(self): ...

# 無需任何手動(dòng)注冊(cè)
print(Handler._registry)
# {'json': <class 'JSONHandler'>, 'xml': <class 'XMLHandler'>}

工廠方法只需查表:

@classmethod
def create(cls, name: str) -> "Handler":
    return cls._registry[name]()

5.2 強(qiáng)制接口約束(抽象檢查的增強(qiáng)版)

abc.ABC 在實(shí)例化時(shí)才報(bào)錯(cuò),__init_subclass__ 可以在類定義時(shí)就報(bào)錯(cuò):

class StrictBase:
    _required_methods = ("execute", "rollback")

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        for method in StrictBase._required_methods:
            if not callable(getattr(cls, method, None)):
                raise TypeError(
                    f"{cls.__name__} must implement '{method}'"
                )

class GoodTransaction(StrictBase):
    def execute(self): ...
    def rollback(self): ...

class BadTransaction(StrictBase):   # 立即 TypeError!
    def execute(self): ...
    # 忘記實(shí)現(xiàn) rollback

5.3 自動(dòng)注入行為(裝飾器的類級(jí)等價(jià)物)

import functools, time

class Timed:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        for name, fn in vars(cls).items():
            if callable(fn) and not name.startswith("_"):
                setattr(cls, name, _timer(fn))

def _timer(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        t = time.perf_counter()
        result = fn(*args, **kwargs)
        print(f"{fn.__name__}: {time.perf_counter() - t:.4f}s")
        return result
    return wrapper

class MyService(Timed):
    def fetch(self): time.sleep(0.1)
    def process(self): time.sleep(0.2)

svc = MyService()
svc.fetch()     # fetch: 0.1002s
svc.process()   # process: 0.2001s

5.4 ORM 字段收集(Django/SQLAlchemy 同款思路)

class Field:
    def __init__(self, col_type):
        self.col_type = col_type

class ModelMeta:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls._fields = {
            k: v for k, v in vars(cls).items()
            if isinstance(v, Field)
        }
        print(f"Model '{cls.__name__}' fields: {list(cls._fields)}")

class User(ModelMeta):
    id = Field("INTEGER")
    name = Field("VARCHAR")
    email = Field("VARCHAR")

# 輸出: Model 'User' fields: ['id', 'name', 'email']

六、與元類的對(duì)比

維度__init_subclass__元類(Metaclass)
語法復(fù)雜度低,普通方法高,需理解 type 體系
作用時(shí)機(jī)子類定義完成后子類定義過程中(可修改類命名空間)
能否修改類命名空間??
多重繼承兼容性好(用 super() 鏈?zhǔn)秸{(diào)用)差(元類沖突是常見陷阱)
傳遞配置關(guān)鍵字參數(shù),優(yōu)雅__new__ 參數(shù),繁瑣
適用場景注冊(cè)、校驗(yàn)、增強(qiáng)需要深度控制類創(chuàng)建過程

結(jié)論: 能用 __init_subclass__ 解決的問題,不必引入元類。

七、與__set_name__的協(xié)同

Python 3.6 同期引入的 __set_name__ 在描述符被賦值給類屬性時(shí)觸發(fā),兩者常配合使用:

class ValidatedField:
    def __set_name__(self, owner, name):
        # 此時(shí) owner 是擁有該描述符的類,name 是屬性名
        self.name = name

    def __set__(self, obj, value):
        if not isinstance(value, int):
            raise TypeError(f"{self.name} must be int")
        obj.__dict__[self.name] = value

class Schema:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        # 可在此對(duì) cls 上的 ValidatedField 做額外處理
        fields = [k for k, v in vars(cls).items()
                  if isinstance(v, ValidatedField)]
        cls._validated_fields = fields

class Config(Schema):
    timeout = ValidatedField()
    retries = ValidatedField()

八、多重繼承下的正確姿勢

多重繼承時(shí),__init_subclass__ 沿 MRO 鏈?zhǔn)秸{(diào)用,必須調(diào)用 super(),否則鏈條斷裂:

class Loggable:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)   # ? 必須
        cls._log = True

class Serializable:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)   # ? 必須
        cls._serializable = True

class Document(Loggable, Serializable):
    pass

# Document._log 和 Document._serializable 均已設(shè)置

若省略 super().__init_subclass__(**kwargs),MRO 中后續(xù)的 __init_subclass__ 將被靜默跳過,引發(fā)難以追蹤的 bug。

九、常見陷阱總結(jié)

1. 忘記調(diào)用 super()
鏈?zhǔn)秸{(diào)用斷裂,多重繼承場景必現(xiàn)問題。

2. 關(guān)鍵字參數(shù)未用 **kwargs 透傳

# 錯(cuò)誤示范
def __init_subclass__(cls, my_param=None):  # 漏掉 **kwargs
    super().__init_subclass__()             # 漏掉 **kwargs

一旦有其他父類也消費(fèi)關(guān)鍵字參數(shù),即報(bào) TypeError

3. 誤以為 cls 是父類
cls 始終是正在被創(chuàng)建的那個(gè)子類,不是定義了 __init_subclass__ 的類。

4. 在 __init_subclass__ 中訪問未完全初始化的子類
某些裝飾器邏輯若依賴子類的 __init__ 已存在,要注意此時(shí)子類的方法已在 vars(cls) 中,但父類方法通過 MRO 繼承,不在 vars(cls) 里。

十、一句話總結(jié)

__init_subclass__ 是 Python 3.6 給類體系提供的輕量級(jí)生命周期鉤子,它在子類被定義的瞬間觸發(fā),讓父類得以觀察、校驗(yàn)、增強(qiáng)乃至注冊(cè)每一個(gè)子類——用最小的復(fù)雜度,實(shí)現(xiàn)了元類 80% 的日常用途。

掌握它,你將擁有一把構(gòu)建插件系統(tǒng)、ORM、接口約束框架的利器,同時(shí)保持代碼的可讀性與可維護(hù)性。

到此這篇關(guān)于深入理解Python __init_subclass__的使用的文章就介紹到這了,更多相關(guān)Python __init_subclass__內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python制作春聯(lián)的示例代碼

    Python制作春聯(lián)的示例代碼

    春聯(lián)是中國傳統(tǒng)文化中最具內(nèi)涵的元素之一,它以對(duì)仗工整、簡潔精巧的文字描繪美好形象,抒發(fā)美好愿望,是中國特有的文學(xué)形式,是華人們過年的重要習(xí)俗。本文將通過Python制作春聯(lián),需要的可以參考一下
    2022-01-01
  • Python爬蟲獲取JavaScript動(dòng)態(tài)渲染后的網(wǎng)頁內(nèi)容四種方法

    Python爬蟲獲取JavaScript動(dòng)態(tài)渲染后的網(wǎng)頁內(nèi)容四種方法

    在爬取動(dòng)態(tài)網(wǎng)頁數(shù)據(jù)時(shí)我們需要模擬客戶端瀏覽器環(huán)境,讓JavaScript能夠正常地執(zhí)行,并獲取渲染后的頁面數(shù)據(jù),這篇文章主要介紹了Python爬蟲獲取JavaScript動(dòng)態(tài)渲染后的網(wǎng)頁內(nèi)容四種方法,需要的朋友可以參考下
    2025-06-06
  • Python使用新浪微博API發(fā)送微博的例子

    Python使用新浪微博API發(fā)送微博的例子

    這篇文章主要介紹了Python使用新浪微博API發(fā)送微博的例子,需要的朋友可以參考下
    2014-04-04
  • OpenCV-Python實(shí)現(xiàn)凸包的獲取

    OpenCV-Python實(shí)現(xiàn)凸包的獲取

    凸包是一個(gè)計(jì)算幾何中的概念,在圖像處理過程中,我們常常需要尋找圖像中包圍某個(gè)物體的凸包,本文就使用OpenCV實(shí)現(xiàn),感興趣的可以了解一下
    2021-06-06
  • python基礎(chǔ)教程項(xiàng)目二之畫幅好畫

    python基礎(chǔ)教程項(xiàng)目二之畫幅好畫

    這篇文章主要為大家詳細(xì)介紹了python基礎(chǔ)教程項(xiàng)目二之畫幅好畫,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • python基礎(chǔ)之類方法和靜態(tài)方法

    python基礎(chǔ)之類方法和靜態(tài)方法

    這篇文章主要介紹了python類方法和靜態(tài)方法,實(shí)例分析了Python中返回一個(gè)返回值與多個(gè)返回值的方法,需要的朋友可以參考下
    2021-10-10
  • Django開發(fā)中的日志輸出的方法

    Django開發(fā)中的日志輸出的方法

    這篇文章主要介紹了Django開發(fā)中的日志輸出的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • python實(shí)現(xiàn)連接mongodb的方法

    python實(shí)現(xiàn)連接mongodb的方法

    這篇文章主要介紹了python實(shí)現(xiàn)連接mongodb的方法,涉及Python使用pymongo模塊的基本技巧,需要的朋友可以參考下
    2015-05-05
  • 詳解Matplotlib繪圖之屬性設(shè)置

    詳解Matplotlib繪圖之屬性設(shè)置

    這篇文章主要介紹了詳解Matplotlib繪圖之屬性設(shè)置,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 基于Python實(shí)現(xiàn)地標(biāo)景點(diǎn)識(shí)別功能

    基于Python實(shí)現(xiàn)地標(biāo)景點(diǎn)識(shí)別功能

    地標(biāo)景點(diǎn)識(shí)別是一種基于計(jì)算機(jī)視覺技術(shù)的應(yīng)用,旨在通過對(duì)圖像進(jìn)行分析和處理,自動(dòng)識(shí)別出圖片中的地標(biāo)景點(diǎn),本文將介紹地標(biāo)景點(diǎn)識(shí)別的背景和原理,并使用Python編程語言來實(shí)現(xiàn)一個(gè)簡單的地標(biāo)景點(diǎn)識(shí)別系統(tǒng),感興趣的朋友可以參考下
    2024-01-01

最新評(píng)論

景洪市| 新竹市| 黄梅县| 调兵山市| 涟水县| 临朐县| 土默特右旗| 霸州市| 泸溪县| 南涧| 滁州市| 若尔盖县| 灵山县| 保康县| 绍兴市| 曲阜市| 阿勒泰市| 连云港市| 花莲县| 兰考县| 晋州市| 龙海市| 瑞安市| 元阳县| 沿河| 蒙自县| 雷波县| 永春县| 柳江县| 清流县| 桑日县| 高碑店市| 尉犁县| 舒城县| 南投县| 温泉县| 天长市| 开鲁县| 新郑市| 噶尔县| 海口市|