首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 類別與 Metaclass 深度解析

     編輯:更多程式設計知識

Python 中「一切皆物件」,類別本身也是物件,其型別為 type。理解類別建構機制,需掌握 type 作為 metaclass 的角色,以及 __metaclass__、類別裝飾器的運作時機差異。

類別也是物件

>>> class C(object):
...     def __init__(self, s): print(s)
...
>>> myclass = C
>>> type(C)
<type 'type'>
>>> myclass(2)  # 可呼叫建立實例
2

類別可指派給變數、傳遞給函式、動態建立:

>>> def make_class(name):
...     class C(object):
...         def print_name(self): print(name)
...     C.__name__ = name
...     return C
...
>>> C1, C2 = map(make_class, ['C1', 'C2'])
>>> C1().print_name()  # C1

Metaclass:建立類別的類別

type(name, bases, attrs) 三參數形式即為動態建立類別:

>>> MyClass = type('MyClass', (object,), {'attr': 0})
>>> type(MyClass)
<type 'type'>

自訂 metaclass 需為可呼叫物件(函式或類別),接受 (name, bases, attrs) 並回傳類別:

>>> def my_meta(name, bases, attrs):
...     print('Creating', name)
...     return type(name, bases, attrs)
...
>>> class C(object):
...     __metaclass__ = my_meta
...
Creating C

實務範例:方法自動記錄裝飾

Metaclass 可在類別建立「前」攔截屬性,為所有可呼叫屬性包裝記錄器:

def log_everything_metaclass(name, bases, attrs):
    new_attrs = {}
    for k, v in attrs.items():
        if hasattr(v, '__call__'):
            new_attrs[k] = logged('%b %d %Y - %H:%M:%S', name + '.')(v)
        else:
            new_attrs[k] = v
    return type(name, bases, new_attrs)

Metaclass vs 類別裝飾器

  • 執行時機:Metaclass 在類別建立「前」執行;裝飾器在建立「後」執行。
  • 修改 __doc__ 等唯讀屬性:Metaclass 可在 attrs 階段修改;裝飾器因類別已建立無法修改。
  • 參數傳遞:裝飾器可直接接收參數;Metaclass 需透過類別屬性傳遞。

繼承 type 建立 Metaclass 類別

class Meta(type):
    def __new__(cls, name, bases, attrs):
        print('Meta.__new__')
        return super(Meta, cls).__new__(cls, name, bases, attrs)
    def __call__(self, *args, **kw):
        print('Meta.__call__')
        return super(Meta, self).__call__(*args, **kw)

完整流程模擬

類別宣告 → Metaclass __new__ → __init__ → 裝飾器 → 類別就緒 → 實例化時 Metaclass __call__ → __new__ → __init__。

實際應用:ID3v2 幀類別工廠

Metaclass 可自動註冊子類別至字典,依幀識別碼快速查找:

frame_map = {}
class FrameFactory(type):
    def __new__(cls, name, bases, attrs):
        new_cls = super(FrameFactory, cls).__new__(cls, name, bases, attrs)
        fid = attrs.get('frame_identifier')
        if fid: frame_map[fid] = new_cls
        return new_cls

class Frame(object):
    __metaclass__ = FrameFactory
    frame_identifier = None

class TitleFrame(Frame):
    frame_identifier = 'TIT2'

同樣功能用裝飾器實作更簡潔,除非需在類別建立前修改屬性,否則建議優先使用裝飾器。

結語

Metaclass 是強大但晦澀的技術,多數情況下裝飾器即可替代。深入理解有助於除錯與框架設計,實務上「三思而後行」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved