ネストされたクラス定義の他のすべての形式と同様に、ネストされたメタクラスは、多くの種類の「本番環境での使用」に対して、より「コンパクトで便利」な場合があります(継承以外でそのメタクラスを再利用しなくても問題ない場合)。デバッグとイントロスペクションには不便です。
__module__
基本的に、メタクラスに適切なトップレベルの名前を付ける代わりに、モジュールで定義されたすべてのカスタムメタクラスが、それらの属性と__name__
属性(Pythonがrepr
ifを形成するために使用するもの)に基づいて互いに区別できないようになります。必要)。検討:
>>> class Mcl(type): pass
...
>>> class A: __metaclass__ = Mcl
...
>>> class B:
... class __metaclass__(type): pass
...
>>> type(A)
<class '__main__.Mcl'>
>>> type(B)
<class '__main__.__metaclass__'>
IOW、「どのタイプがクラスAであるか」(メタクラスはクラスのタイプです、覚えておいてください)を調べたい場合は、明確で有用な答えが得られMcl
ます。これはメインモジュールにあります。ただし、「どのタイプがクラスBであるか」を調べたい場合、答えはそれほど有用ではありません。モジュール内__metaclass__
にあると表示されますが、それは真実ではありません。main
>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>>
...実際にはそのようなことはありません。そのreprは誤解を招きやすく、あまり役に立ちません;-)。
クラスのreprは本質的に'%s.%s' % (c.__module__, c.__name__)
-シンプルで便利で一貫性のあるルール-ですが、多くの場合、class
ステートメントがモジュールスコープで一意ではない、またはモジュールスコープにまったくない(関数またはクラス本体内ではない)などです。 )、または存在しない場合(もちろんclass
、メタクラスを明示的に呼び出すことにより、クラスをステートメントなしで構築できます)、これは多少誤解を招く可能性があります(そして、最善の解決策は、これらの特殊なケースを可能な限り回避することです。それらを使用することにより、実質的な利点を得ることができます)。たとえば、次のことを考慮してください。
>>> class A(object):
... def foo(self): print('first')
...
>>> x = A()
>>> class A(object):
... def foo(self): print('second')
...
>>> y = A()
>>> x.foo()
first
>>> y.foo()
second
>>> x.__class__
<class '__main__.A'>
>>> y.__class__
<class '__main__.A'>
>>> x.__class__ is y.__class__
False
同じスコープに2つのclass
ステートメントがある場合、2番目のステートメントは名前(ここではA
)を再バインドしますが、既存のインスタンスは名前ではなくオブジェクトによる名前の最初のバインドを参照します。したがって、両方のクラスオブジェクトが残り、一方はtype
(または__class__
そのインスタンスの属性)(存在する場合-存在しない場合、その最初のオブジェクトは消えます)-2つのクラスは同じ名前とモジュール(したがって同じ表現)を持ちますが、それらは別個のオブジェクトです。クラスまたは関数本体内にネストされたクラス、またはメタクラス(を含むtype
)を直接呼び出すことによって作成されたクラスは、デバッグまたはイントロスペクションが必要になった場合に同様の混乱を引き起こす可能性があります。
したがって、メタクラスをネストすることは、そのコードをデバッグしたり、他の方法でイントロスペクトしたりする必要がない場合は問題ありません。また、メタクラスを実行している人がこの癖を理解していれば、一緒に暮らすことができます(ただし、素敵な本名を使用するほど便利ではありませんが、もちろん、でコーディングされた関数をデバッグするのと同じように、でコーディングされた関数をデバッグするlambda
ほど便利なことはありませんdef
。lambda
vsとの類推によりdef
、匿名の「ネストされた」定義は、デバッグや内省がおそらく必要とされないほど非常に単純なメタクラスに対しては問題ないと合理的に主張できます。
Python 3では、「ネストされた定義」は機能しません。そこでは、のようにメタクラスをキーワード引数としてクラスに渡す必要があるため、本文でclass A(metaclass=Mcl):
定義しても効果はありません。__metaclass__
これは、Python 2コードのネストされたメタクラス定義がおそらく適切であるのは、コードをPython 3に移植する必要がないことが確実である場合にのみであると私は信じています(移植を非常に難しくしているため、目的のためにメタクラス定義をネスト解除します)-「使い捨て」コード、言い換えると、Python 3の一部のバージョンが速度、機能、またはサードの巨大で説得力のある利点を獲得する数年後には存在しません- Python 2.7(Python 2の最後のバージョン)を介したパーティのサポート。
コンピューティングの歴史が示すように、使い捨てになると予想されるコードには、あなたを完全に驚かせるという愛らしい習慣があり、まだ約20年後です(おそらく、「昔から」同じ時期に書いたコードは完全に忘れた;-)。これは確かに、メタクラスのネストされた定義を回避することを示唆しているように思われます。