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

Python 元類(中):攔截類的創(chuàng)建方法

 更新時間:2026年05月29日 09:22:30   作者:Hanniel  
這段文章介紹了Python中的元元類的概念及其應(yīng)用法,通過自定義元元類可以攔截類類創(chuàng)建過程、自動注入屬性、校驗類定義、實現(xiàn)單例模式和插件自動注冊等功能,極大增強(qiáng)了類定義的靈活性與可控性,感興趣的朋友一起看看吧

上篇我們知道了:type 是一個類工廠,所有類都是它創(chuàng)建的。class 語句本質(zhì)上就是一次 type() 調(diào)用。

那如果我們自己寫一個類工廠,替掉 type,會怎樣?

第一個元類

元類就是一個繼承了 type 的類。你重寫它的 __new__ 方法,就能在類創(chuàng)建的時候插一腳:

class SimpleMeta(type):
    def __new__(mcs, name, bases, namespace):
        print(f"  正在創(chuàng)建類: {name}")
        cls = super().__new__(mcs, name, bases, namespace)
        return cls
class MyClass(metaclass=SimpleMeta):
    pass
# 輸出: 正在創(chuàng)建類: MyClass
print(type(MyClass))  # <class '__main__.SimpleMeta'> —— 不再是 type

就這么簡單。metaclass=SimpleMeta 告訴 Python:「創(chuàng)建 MyClass 的時候,別用默認(rèn)的 type(),用我的 SimpleMeta()」。

你可以多創(chuàng)建幾個類,看看效果:

class Another(metaclass=SimpleMeta):
    pass
class AndAnother(metaclass=SimpleMeta):
    pass
# 輸出:
#   正在創(chuàng)建類: Another
#   正在創(chuàng)建類: AndAnother

每一個用 SimpleMeta 創(chuàng)建的類,都會經(jīng)過 __new__。這就是元類的核心——攔截類的創(chuàng)建過程。

參數(shù)是什么意思?

__new__ 里的四個參數(shù),和 type() 的三個參數(shù)一一對應(yīng):

參數(shù)含義來源
mcs元類本身Python 自動傳入
name正在創(chuàng)建的類名對應(yīng) type() 的第一個參數(shù)
bases父類元組對應(yīng) type() 的第二個參數(shù)
namespace類體中定義的所有屬性和方法的字典對應(yīng) type() 的第三個參數(shù)

你可以把 namespace 打印出來看看:

class DebugMeta(type):
    def __new__(mcs, name, bases, namespace):
        print(f"  namespace = {namespace}")
        return super().__new__(mcs, name, bases, namespace)
class Sample(metaclass=DebugMeta):
    x = 10
    def hello(self):
        return "hi"
# 輸出:
# namespace = {'__module__': '__main__', '__qualname__': 'Sample', 'x': 10, 'hello': <function Sample.hello at 0x...>}

x = 10def hello 都在字典里。你可以讀它、改它、甚至拒絕創(chuàng)建這個類。

實戰(zhàn)一:自動注入屬性

理解了參數(shù),我們來做點實用的事情。假設(shè)你有一批類都需要一個 version 屬性,但你不想每個類都手動寫:

class AutoAttrMeta(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if not hasattr(cls, 'version'):
            cls.version = "1.0"
        return cls
class Service(metaclass=AutoAttrMeta):
    pass
class AdvancedService(Service):
    version = "2.0"  # 自己定義了,不會被覆蓋
class BasicService(metaclass=AutoAttrMeta):
    pass
print(Service.version)         # 1.0  ← 自動加上了
print(AdvancedService.version) # 2.0  ← 自己定義的,沒被覆蓋
print(BasicService.version)    # 1.0  ← 也自動加上了

邏輯很簡單:類創(chuàng)建完之后,檢查一下有沒有 version,沒有就補(bǔ)上。

這就是元類最典型的用法——在類創(chuàng)建的那一刻,自動做一些事情。Django 的 Model 類就是這么干的:你寫 class User(models.Model): 的時候,元類在背后自動幫你加了 __tablename__、__fields__ 一堆東西,你根本不用操心。

實戰(zhàn)二:在類定義時就攔住錯誤

元類還能做校驗。與其讓代碼跑到一半才發(fā)現(xiàn)字段名寫錯了,不如在類定義的時候就報錯:

class LowercaseMeta(type):
    def __new__(mcs, name, bases, namespace):
        for attr_name in namespace:
            if not attr_name.startswith('_') and attr_name != attr_name.lower():
                raise TypeError(
                    f"類 '{name}' 中的屬性 '{attr_name}' 必須全小寫!"
                )
        return super().__new__(mcs, name, bases, namespace)
class GoodClass(metaclass=LowercaseMeta):
    my_field = 1       # ? 小寫
    another_field = 2  # ? 小寫
print(GoodClass.my_field)  # 1 —— 沒問題
try:
    class BadClass(metaclass=LowercaseMeta):
        BadField = 1   # ? 大寫了!
except TypeError as e:
    print(e)  # 類 'BadClass' 中的屬性 'BadField' 必須全小寫!

BadField 在類定義的那一刻就被攔下來了。這個錯誤不會等到你調(diào)用某個方法時才冒出來——它在模塊被 import 的時候就會發(fā)生,非常早。

實戰(zhàn)三:單例模式

單例的意思是:一個類只能有一個實例。不管你調(diào)用多少次 Database(),拿到的都是同一個對象。

要實現(xiàn)這個,我們需要攔截的不是類的創(chuàng)建,而是實例的創(chuàng)建。

上篇我們講過,當(dāng)你寫 Foo() 的時候,Python 實際上是在調(diào)用 type.__call__()。這個 __call__ 方法會依次調(diào)用類的 __new____init__。元類可以重寫這個 __call__,在實例化時插入自己的邏輯:

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            print(f"  創(chuàng)建 {cls.__name__} 的唯一實例")
            cls._instances[cls] = super().__call__(*args, **kwargs)
        else:
            print(f"  返回已有實例")
        return cls._instances[cls]
class Database(metaclass=SingletonMeta):
    def __init__(self):
        self.connection_id = id(self)
db1 = Database()  # 創(chuàng)建 Database 的唯一實例
db2 = Database()  # 返回已有實例
print(db1 is db2)                        # True —— 同一個對象
print(db1.connection_id == db2.connection_id)  # True

調(diào)用鏈?zhǔn)沁@樣的:

Database()
    │
    ▼
SingletonMeta.__call__(Database)   ← 元類攔截
    │
    ├─ 第一次 → super().__call__() → Database.__new__() → Database.__init__()
    │
    └─ 之后 → 直接返回緩存的實例

注意:元類的 __call__ 攔截的是 類() 這個調(diào)用,而類的 __new____init__ 負(fù)責(zé)實際創(chuàng)建和初始化實例。元類站在它們「上面」,決定要不要調(diào)用它們。

實戰(zhàn)四:插件自動注冊

最后一個實戰(zhàn)例子——插件自動注冊。你定義一個子類,它就自動出現(xiàn)在注冊表里,不需要手動調(diào)用任何注冊函數(shù):

class PluginMeta(type):
    registry = {}
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if name != "Base":          # 跳過基類本身
            mcs.registry[name] = cls
        return cls
class Base(metaclass=PluginMeta):
    pass
class AuthPlugin(Base):
    def authenticate(self):
        return "authenticated"
class LogPlugin(Base):
    def log(self, msg):
        return f"LOG: {msg}"
# 不需要手動注冊,定義類的時候就完成了
print(list(PluginMeta.registry.keys()))  # ['AuthPlugin', 'LogPlugin']
# 可以通過名字動態(tài)實例化
plugin = PluginMeta.registry["AuthPlugin"]()
print(plugin.authenticate())  # authenticated

這種模式在實際項目中非常常見:插件系統(tǒng)、策略模式、序列化框架按類型名找處理器……本質(zhì)上都是「定義即注冊」。

一個關(guān)鍵的細(xì)節(jié):執(zhí)行時機(jī)

元類的代碼,在什么時候跑?

在類定義的時候,不是實例化的時候。

class TraceMeta(type):
    def __new__(mcs, name, bases, namespace):
        print(f"  [元類] 正在創(chuàng)建 {name}")
        return super().__new__(mcs, name, bases, namespace)
print("開始定義類...")
class A(metaclass=TraceMeta):
    pass
print("類定義完成")
# 輸出:
# 開始定義類...
#   [元類] 正在創(chuàng)建 A
# 類定義完成

模塊被 import 的那一刻,元類就已經(jīng)跑完了。之后你 A() 實例化的時候,元類的 __new__ 不會再執(zhí)行(但 __call__ 會,如果你重寫了的話)。

回顧一下

到這里你已經(jīng)知道了:

  1. 元類就是一個繼承 type 的類工廠,重寫 __new__ 可以在類創(chuàng)建時做任何事情
  2. 重寫 __call__ 可以攔截實例化,控制 __new____init__ 是否被調(diào)用
  3. 元類代碼在類定義時就執(zhí)行,不是實例化時

但元類的能力不止于此。它還能控制類的命名空間、自定義 isinstance 的行為、處理繼承鏈上的元類沖突……

下一篇,我們講這些進(jìn)階內(nèi)容,以及一個更重要的問題:什么時候不該用元類。

到此這篇關(guān)于Python 元類(中):攔截類的創(chuàng)建方法的文章就介紹到這了,更多相關(guān)Python攔截類的創(chuàng)建內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python利用appium實現(xiàn)模擬手機(jī)滑動操控的操作

    Python利用appium實現(xiàn)模擬手機(jī)滑動操控的操作

    Appium 是一個開源、跨平臺的自動化測試工具,用于測試原生和輕量移動應(yīng)用,支持 iOS, Android 和 FirefoxOS 平臺。本文將利用appium實現(xiàn)模擬手機(jī)滑動操控的操作,感興趣的可以了解一下
    2022-07-07
  • 使用Pycharm為項目創(chuàng)建一個虛擬環(huán)境完整圖文教程

    使用Pycharm為項目創(chuàng)建一個虛擬環(huán)境完整圖文教程

    這篇文章主要給大家介紹了關(guān)于使用Pycharm為項目創(chuàng)建一個虛擬環(huán)境的相關(guān)資料,我們在使用pycharm做項目時,最好給每一個工程都創(chuàng)建一個虛擬環(huán)境,將對應(yīng)的安裝包放在該虛擬環(huán)境中,避免項目與項目之間產(chǎn)生關(guān)系或沖突,便于管理,需要的朋友可以參考下
    2023-09-09
  • 詳解Pytorch自動求導(dǎo)機(jī)制

    詳解Pytorch自動求導(dǎo)機(jī)制

    自動求導(dǎo)是一種計算梯度的技術(shù),它允許我們在定義模型時不需要手動推導(dǎo)梯度計算公式,PyTorch 提供了自動求導(dǎo)的功能,使得梯度的計算變得非常簡單和高效,這篇文章主要介紹了Pytorch自動求導(dǎo)機(jī)制詳解,需要的朋友可以參考下
    2023-07-07
  • Python xlrd模塊導(dǎo)入過程及常用操作

    Python xlrd模塊導(dǎo)入過程及常用操作

    這篇文章主要介紹了Python xlrd模塊導(dǎo)入過程及常用操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • 關(guān)于Python3 lambda函數(shù)的深入淺出

    關(guān)于Python3 lambda函數(shù)的深入淺出

    今天小編就為大家分享一篇關(guān)于Python3 lambda函數(shù)的深入淺出,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • Pandas-DataFrame知識點匯總

    Pandas-DataFrame知識點匯總

    這篇文章主要介紹了Pandas-DataFrame知識點匯總,DataFrame是一種表格型數(shù)據(jù)結(jié)構(gòu),它含有一組有序的列,每列可以是不同的值,下面我們一起進(jìn)入文章了解更多詳細(xì)內(nèi)容吧,需要的小伙伴也可以參考一下
    2022-03-03
  • Python實現(xiàn)壓縮文件夾與解壓縮zip文件的方法

    Python實現(xiàn)壓縮文件夾與解壓縮zip文件的方法

    這篇文章主要介紹了Python實現(xiàn)壓縮文件夾與解壓縮zip文件的方法,涉及Python使用zipfile模塊進(jìn)行zip文件壓縮與解壓縮相關(guān)操作技巧,需要的朋友可以參考下
    2018-09-09
  • 在python?腳本下解析json數(shù)據(jù)

    在python?腳本下解析json數(shù)據(jù)

    這篇文章主要介紹了在python?腳本下解析json數(shù)據(jù),json數(shù)據(jù)包括JSONObject和JSONArray,下文關(guān)于其解析的內(nèi)容需要的小伙伴可以參考一下
    2022-04-04
  • Python使用keras和tensorflow遇到的問題及解決

    Python使用keras和tensorflow遇到的問題及解決

    這篇文章主要介紹了Python使用keras和tensorflow遇到的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 詳解Python高階函數(shù)

    詳解Python高階函數(shù)

    這篇文章主要介紹了Python高階函數(shù)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)python,感興趣的朋友可以了解下
    2020-08-08

最新評論

绥化市| 名山县| 尚志市| 临江市| 宁国市| 潞西市| 济南市| 福贡县| 兴义市| 横峰县| 望江县| 怀宁县| 沁水县| 天津市| 阳曲县| 灵山县| 鹤山市| 巴彦淖尔市| 穆棱市| 绥芬河市| 伊金霍洛旗| 高台县| 安溪县| 盱眙县| 宁安市| 文山县| 玉树县| 寿阳县| 台中县| 十堰市| 舞阳县| 建平县| 武城县| 余江县| 广东省| 安丘市| 株洲市| 丹棱县| 靖安县| 扎囊县| 福贡县|