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

深入理解Python __init_subclass__的使用

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

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

當你定義一個基類,希望所有子類在被定義時(而非實例化時)就自動完成某些注冊、校驗或增強邏輯,你會怎么做?

傳統(tǒng)方案是元類(metaclass),但元類的心智負擔極重。Python 3.6 引入的 __init_subclass__ 正是為了解決這一痛點——用一個普通的類方法,優(yōu)雅地攔截子類的創(chuàng)建過程。

二、它是什么

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

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

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

三、調(diào)用時機與調(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'>

關鍵細節(jié):

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

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

__init_subclass__ 最精妙的設計之一是支持通過 class 語句的關鍵字參數(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

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

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

五、核心應用場景

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

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

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): ...

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

工廠方法只需查表:

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

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

abc.ABC 在實例化時才報錯,__init_subclass__ 可以在類定義時就報錯:

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): ...
    # 忘記實現(xiàn) rollback

5.3 自動注入行為(裝飾器的類級等價物)

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']

六、與元類的對比

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

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

七、與__set_name__的協(xié)同

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

class ValidatedField:
    def __set_name__(self, owner, name):
        # 此時 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)
        # 可在此對 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()

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

多重繼承時,__init_subclass__ 沿 MRO 鏈式調(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 均已設置

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

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

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

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

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

一旦有其他父類也消費關鍵字參數(shù),即報 TypeError。

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

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

十、一句話總結(jié)

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

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

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

相關文章

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

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

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

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

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

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

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

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

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

    python基礎教程項目二之畫幅好畫

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

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

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

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

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

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

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

    詳解Matplotlib繪圖之屬性設置

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

    基于Python實現(xiàn)地標景點識別功能

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

最新評論

通榆县| 清苑县| 阿瓦提县| 顺昌县| 北宁市| 芮城县| 牟定县| 乌拉特中旗| 新郑市| 宿州市| 东阿县| 赤壁市| 新安县| 博罗县| 衢州市| 满城县| 永宁县| 汉源县| 泰兴市| 林周县| 宁德市| 湟源县| 福安市| 峨眉山市| 正阳县| 札达县| 商水县| 米泉市| 上栗县| 凤冈县| 台北市| 宜都市| 延川县| 包头市| 德惠市| 新乡县| 论坛| 上杭县| 壤塘县| 科尔| 方山县|