Python進階:metaclass談

来源:https://www.cnblogs.com/xiaoguanqiu/archive/2019/06/23/11074603.html
-Advertisement-
Play Games

metaclass 的超越變形特性有什麼用? 來看yaml的實例: import yaml class Monster(yaml.YAMLObject): yaml_tag = u'!Monster' def __init__(self, name, hp, ac, attacks): self.n ...


metaclass 的超越變形特性有什麼用?

  來看yaml的實例:
import yaml
class Monster(yaml.YAMLObject):
  yaml_tag = u'!Monster'
  def __init__(self, name, hp, ac, attacks):
    self.name = name
    self.hp = hp
    self.ac = ac
    self.attacks = attacks
  def __repr__(self):
    return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
       self.__class__.__name__, self.name, self.hp, self.ac,      
       self.attacks)

monster1 = yaml.load("""
--- !Monster
name: Cave spider
hp: [2,6]    # 2d6
ac: 16
attacks: [BITE, HURT]
""",Loader=yaml.Loader)

print(monster1)
#Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
print(type(monster1)) #<class '__main__.Monster'>


print (yaml.dump(Monster(
    name='Cave lizard', hp=[3,6], ac=16, attacks=['BITE','HURT']))
)

# dump() 返回 str
# 輸出
# !Monster
# ac: 16
# attacks: [BITE, HURT]
# hp: [3, 6]
# name: Cave lizard

  上面的代碼調用yaml.load(),就能把任意一個 yaml 序列載入成一個 Python Object;而調用yaml.dump(),就能把一個 YAMLObject 子類序列化。對於 load() 和 dump() 的使用者來說,他們完全不需要提前知道任何類型信息,這讓超動態配置編程成了可能。

  只要簡單地繼承 yaml.YAMLObject,就能讓你的 Python Object 具有序列化和逆序列化能力。  

metaclass 的超越變形特性怎麼用?

  YAML 怎樣用 metaclass 實現動態序列化 / 逆序列化功能,看其源碼

#Python 2/3 相同部分
class YAMLObjectMetaclass(type):
  def __init__(cls, name, bases, kwds):
    super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
    if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
      cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
  # 省略其餘定義

# Python 3
class YAMLObject(metaclass=YAMLObjectMetaclass):
  yaml_loader = Loader
  # 省略其餘定義

# Python 2
class YAMLObject(object):
  __metaclass__ = YAMLObjectMetaclass
  yaml_loader = Loader
  # 省略其餘定義

  YAMLObject 把 metaclass 都聲明成了 YAMLObjectMetaclass

  在你定義任何 YAMLObject 子類時,Python 會強行插入運行下麵這段代碼
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)

 

Python 底層語言設計層面是如何實現 metaclass 的?

  第一,所有的 Python 的用戶定義類,都是 type 這個類的實例。

class MyClass:
  pass

instance = MyClass()

print(type(instance))
# 輸出
#<class '__main__.MyClass'>

print(type(MyClass))
# 輸出
#<class 'type'>

  instance 是 MyClass 的實例,而 MyClass 不過是“上帝”type 的實例。

     第二,用戶自定義類,只不過是 type 類的__call__運算符重載。  
class MyClass:
  data = 1
  
instance = MyClass()
print(MyClass, instance)
# 輸出
#(__main__.MyClass, <__main__.MyClass instance at 0x7fe4f0b00ab8>)
print(instance.data)
# 輸出
#1

MyClass = type('MyClass', (), {'data': 1})
instance = MyClass()
print(MyClass, instance)
# 輸出
#(__main__.MyClass, <__main__.MyClass at 0x7fe4f0aea5d0>)

print(instance.data)
# 輸出
#1

  可以看出,定義Myclass的時候Python實際調用的是type(classname, superclasses, attributedict),就是 type 的__call__運算符重載,接著會進一步調用

type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

    

  第三,metaclass 是 type 的子類,通過替換 type 的__call__運算符重載機制,“超越變形”正常的類。   一旦你把一個類型 MyClass 的 metaclass 設置成 MyMeta,MyClass 就不再由原生的 type 創建,而是會調用 MyMeta 的__call__運算符重載。
class = type(classname, superclasses, attributedict) 
# 變為了
class = MyMeta(classname, superclasses, attributedict)

  

使用 metaclass 的風險

  正如你所看到的那樣,metaclass 會"扭曲變形"正常的 Python 類型模型。所以,如果使用不慎,對於整個代碼庫造成的風險是不可估量的。換句話說,metaclass 僅僅是給小部分 Python 開發者,在開發框架層面的 Python 庫時使用的。而在應用層,metaclass 往往不是很好的選擇。

  

參考

  極客時間《Python 核心技術與實戰》

class Mymeta(type):
    def __init__(self, name, bases, dic):
        super().__init__(name, bases, dic)
        print('===>Mymeta.__init__')
        print(self.__name__)
        print(dic)
        print(self.yaml_tag)

    def __new__(cls, *args, **kwargs):
        print('===>Mymeta.__new__')
        print(cls.__name__)
        return type.__new__(cls, *args, **kwargs)

    def __call__(cls, *args, **kwargs):
        print('===>Mymeta.__call__')
        obj = cls.__new__(cls)
        obj.testPerporet = 'change' #修改子類的屬性
        cls.__init__(cls, *args, **kwargs)
        return obj
    
class Foo(metaclass=Mymeta):
    yaml_tag = '!Foo'
    testPerporet = 'orig'

    def __init__(self, name):
        print('Foo.__init__')
        self.name = name

    def __new__(cls, *args, **kwargs):
        print('Foo.__new__')
        return object.__new__(cls)

foo = Foo('foo')
print(foo.__dict__)
塵墨 提供的參考代碼

 

 

 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • static是java中很常用的一個關鍵字,使用場景也很多,本文主要介紹了它的五種用法,static變數,static方法,static代碼塊,static內部類,static包內導入,在一定環境下使用,可以提高程式的運行性能,優化程式的結構 ...
  • 所屬網站分類: 資源下載 > python視頻教程 作者:外星人入侵 鏈接: http://www.pythonheidong.com/blog/article/435/ 來源:python黑洞網 www.pythonheidong.com python入門教程-1-Python編程語言歷史及特性. ...
  • 高考完後這麼就才想起這系列教程,實在抱歉,現在該來繼續教程了。 本節利用前面所學知識,來完成一個小工具——文本編輯器! ...
  • 1.一維數組的聲明與初始化 正確的方式: 錯誤的方式 2.一維數組元素的引用:通過角標的方式調用。 3.數組的屬性:length 說明: 4.一維數組的遍歷 5.一維數組元素的預設初始化值 6.一維數組的記憶體解析 : ...
  • 我們都知道Java中的繼承是復用代碼、擴展子類的一種方式,繼承使得Java中重覆的代碼能夠被提取出來供子類共用,對於Java程式的性能以及修改和擴展有很大的意義,所以這是一個非常重要的知識點。 那麼對於繼承的知識點,你真的都瞭解了嗎? 首先,我們都知道子類繼承父類,就能直接訪問父類的公共屬性以及受保 ...
  • 一、給定一個整型數組,包括正負值,找出取任意三個值的乘積最大 1、對整型排序(這裡使用堆排序) 2、最大值只能是最小兩個和最大一個或者最大三個值的乘積 3、測試 ...
  • 單表查詢的語法及關鍵字執行的優先順序 單表查詢語法 關鍵字執行的優先順序 1. 找到表: from 2. 拿著where指定的約束條件,去文件 / 表中取出一條條記錄 3. 將取出的一條條記錄進行分組group by , 如果沒有group by ,則整體作為一組 4. 執行select (distin ...
  • 新聞 "Azure Notebook概覽" "SpecFlow 3就在這裡了!" "使用新的Try .NET模版創建互動式文檔" "逐漸演化的.NET Core框架" "Dylan與Linebreakers Oslo 2019" 視頻及幻燈片 "F MonoGame平臺游戲系列:平鋪背景" "我愛F ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...