カスタムメタクラス
これまでのところ、メタクラスが何であるかはすでにわかっています。したがって、メタクラスの用途が最初から最後までまだわかりません。メタクラスについて學(xué)びました。その使用法を理解する前に、まずメタクラスをカスタマイズする方法を理解しましょう。カスタマイズ方法を理解することによってのみ、その機能をより深く理解できるからです。
最初に __metaclass__ 屬性について理解しましょう
metaclass (直訳するとメタクラス) を簡単に説明します。
class を定義すると、次のことができます。このクラスに基づいてインスタンスを作成するため、最初にクラスを定義してからインスタンスを作成します。
しかし、クラスを作成したい場合はどうすればよいでしょうか?次に、メタクラスに基づいてクラスを作成する必要があるため、最初にメタクラスを定義してからクラスを作成します。
接続は次のとおりです。最初にメタクラスを定義し、次にクラスを作成し、最後にインスタンスを作成します。
つまり、メタクラスを使用すると、クラスを作成したり、クラスを変更したりできます。つまり、クラスはメタクラスによって作成される「インスタンス」と考えることができます。
class MyObject(object): __metaclass__ = something… […]
このように書くと、Python はメタクラスを使用してクラス MyObject を作成します。 class MyObject(object) を作成したとき、クラス オブジェクト MyObject はまだメモリ內(nèi)に作成されていません。 Python はクラス定義で __metaclass__ 屬性を探します。それが見つかった場合、Python はそれを使用してクラス MyObject を作成します。見つからない場合は、組み込みの型関數(shù)を使用してクラスを作成します。それでも理解できない場合は、以下のフローチャートを見てください。
別の例:
class Foo(Bar): pass
その判斷プロセスは何ですか?
まず、Foo に __metaclass__ 屬性があるかどうかを確認(rèn)します。存在する場合、Python は __metaclass__ を通じてメモリ內(nèi)に Foo という名前のクラス オブジェクトを作成します (これはクラス オブジェクトであることに注意してください)。 Python が __metaclass__ を見つけられない場合、引き続き Bar (親クラス) で __metaclass__ 屬性を探し、以前と同じ操作を?qū)g行しようとします。 Python が親クラスで __metaclass__ を見つけられない場合、モジュール階層で __metaclass__ を探し、同じことを試みます。それでも __metaclass__ が見つからない場合、Python は組み込み型を使用してクラス オブジェクトを作成します。
実際、 __metaclass__ はクラスの動作を定義します。クラスがインスタンスの動作を定義する方法と同様に、メタクラスはクラスの動作を定義します。クラスはメタクラスのインスタンスであると言えます。
これで、__metaclass__ 屬性の基本は理解できましたが、この屬性の使用方法や、この屬性に何を入力できるかについてはまだ説明していませんでした。
答えは、クラスを作成できるということです。では、クラスを作成するには何を使用できるのでしょうか? type、または type またはサブクラス type を使用するもの。
メタクラスの主な目的は、クラスの作成時に自動的にクラスを変更することです。通常、API に対して次のようなことを行い、現(xiàn)在のコンテキストに適合するクラスを作成します。モジュール內(nèi)のすべてのクラス屬性を大文字にする必要があるという愚かな例を想像してください。これを行うにはいくつかの方法がありますが、その 1 つはモジュール レベルで __metaclass__ を設(shè)定することです。このメソッドを使用すると、このモジュール內(nèi)のすべてのクラスがこのメタクラスを通じて作成されます。すべての屬性を大文字に変更するようにメタクラスに指示するだけで、すべて問題なく実行されます。
幸いなことに、__metaclass__ は実際には任意に呼び出すことができ、正式なクラスである必要はありません。それでは、例として単純な関數(shù)から始めましょう。
# 元類會自動將你通常傳給‘type’的參數(shù)作為自己的參數(shù)傳入 def upper_attr(future_class_name, future_class_parents, future_class_attr): '''返回一個類對象,將屬性都轉(zhuǎn)為大寫形式''' # 選擇所有不以'__'開頭的屬性 attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) # 將它們轉(zhuǎn)為大寫形式 uppercase_attr = dict((name.upper(), value) for name, value in attrs) # 通過'type'來做類對象的創(chuàng)建 return type(future_class_name, future_class_parents, uppercase_attr) __metaclass__ = upper_attr # 這會作用到這個模塊中的所有類 class Foo(object): # 我們也可以只在這里定義__metaclass__,這樣就只會作用于這個類中 bar = 'bip' print hasattr(Foo, 'bar') # 輸出: False print hasattr(Foo, 'BAR') # 輸出:True f = Foo() print f.BAR # 輸出:'bip' 用 class 當(dāng)做元類的做法: # 請記住,'type'實際上是一個類,就像'str'和'int'一樣 # 所以,你可以從type繼承 class UpperAttrMetaClass(type): # __new__ 是在__init__之前被調(diào)用的特殊方法 # __new__是用來創(chuàng)建對象并返回之的方法 # 而__init__只是用來將傳入的參數(shù)初始化給對象 # 你很少用到__new__,除非你希望能夠控制對象的創(chuàng)建 # 這里,創(chuàng)建的對象是類,我們希望能夠自定義它,所以我們這里改寫__new__ # 如果你希望的話,你也可以在__init__中做些事情 # 還有一些高級的用法會涉及到改寫__call__特殊方法,但是我們這里不用 def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return type(future_class_name, future_class_parents, uppercase_attr)
ただし、このメソッドは実際には OOP ではありません。 type を直接呼び出し、親クラスの __new__ メソッドをオーバーライドしませんでした。次のようにしてみましょう:
class UpperAttrMetaclass(type): def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) # 復(fù)用type.__new__方法 # 這就是基本的OOP編程,沒什么魔法 return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)
追加のパラメータ upperattr_metaclass があることに気づいたかもしれませんが、これは特別なことではありません。クラス メソッドの最初の引數(shù)は、通常のクラス メソッドの self 引數(shù)と同様に、常に現(xiàn)在のインスタンスを表します。もちろん、わかりやすくするために、ここでは名前を長くしました。ただし、self と同様に、すべてのパラメータには従來の名前が付いています。実際の運用コードでは、メタクラスは次のようになります。
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__') uppercase_attr = dict((name.upper(), value) for name, value in attrs) return type.__new__(cls, name, bases, uppercase_attr)
スーパー メソッドを使用すると、継承が容易になります (はい、メタクラスを持ち、メタクラスから継承し、タイプから継承)
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
通常、私たちはメタクラスを使用して、イントロスペクションに依存したり、継承を制御したりするなど、あいまいなことを?qū)g行します。実際、メタクラスを使用して「暗い魔法」を?qū)g行し、複雑なものを作成するのは特に便利です。しかし、メタクラス自體に関して言えば、実際には非常に単純です。
インターセプト クラスの作成
クラスの変更
変更されたクラスに戻る