web-dev-qa-db-ja.com

Python Metaclass: 'with_metaclass()'を理解する

クラスの定義でwith_metaclass()呼び出しが何を意味するのかを聞きたいです。

例えば。:

class Foo(with_metaclass(Cls1, Cls2)):
  • クラスがメタクラスを継承する特殊なケースですか?
  • 新しいクラスもメタクラスですか?
45
Zakos

with_metaclass() は、 sixライブラリ が提供するユーティリティクラスファクトリ関数で、Python 2および3。

一時的なメタクラスで少し手を使用して(以下を参照)、Python 2とPython 3。

ドキュメントから引用:

基本クラスbaseとメタクラスmetaclassを使用して新しいクラスを作成します。これは、次のようなクラス宣言で使用されるように設計されています。

from six import with_metaclass

class Meta(type):
    pass

class Base(object):
    pass

class MyClass(with_metaclass(Meta, Base)):
    pass

メタクラスをアタッチする構文がPython 2と3の間で変更されたため、これが必要です。

Python 2:

class MyClass(object):
    __metaclass__ = Meta

Python 3:

class MyClass(metaclass=Meta):
    pass

with_metaclass()関数は、メタクラスがa)サブクラスに継承され、b)メタクラスを使用して新しいクラスを生成できること、およびc)メタクラスを持つ基本クラスからサブクラスを作成すると、実際のサブクラスオブジェクトはメタクラスに委任されます。一時的なmetaclassメタクラスを持つ新しい一時的な基本クラスを効果的に作成します。サブクラスの作成に使用すると、swaps out一時的な基本クラスとメタクラスを選択したメタクラスと組み合わせます。

def with_metaclass(meta, *bases):
    """Create a base class with a metaclass."""
    # This requires a bit of explanation: the basic idea is to make a dummy
    # metaclass for one level of class instantiation that replaces itself with
    # the actual metaclass.
    class metaclass(type):

        def __new__(cls, name, this_bases, d):
            return meta(name, bases, d)

        @classmethod
        def __prepare__(cls, name, this_bases):
            return meta.__prepare__(name, bases)
    return type.__new__(metaclass, 'temporary_class', (), {})

上記の内訳:

  • type.__new__(metaclass, 'temporary_class', (), {})metaclassメタクラスを使用して、temporary_classという名前の新しいクラスオブジェクトを作成します。 type.__new__(metaclass, ...)の代わりにmetaclass(...)を使用して、次のステップでのわずかな作業に必要な特別なmetaclass.__new__()実装の使用を回避します。
  • Python 3のみ、temporary_classが基本クラスとして使用される場合、Python最初の呼び出しmetaclass.__prepare__()(派生クラス名、(temporary_class,)this_bases引数としてintendedメタクラスmetaを使用してmeta.__prepare__()を呼び出し、this_basesを無視してbases引数を渡します。
  • 次に、metaclass.__prepare__()の戻り値をクラス属性のベース名前空間として使用した後(またはPython 2)の場合は単純な辞書を使用します)、Pythonは、実際のクラスを作成するためにmetaclass.__new__()を呼び出します。これは(temporary_class,)としてthis_basesタプルとして渡されますが、上記のコードはこれを無視し、代わりにbasesを使用してmeta(name, bases, d)新しい派生クラスを作成します。

その結果、with_metaclass()を使用すると、新しいクラスオブジェクトが得られます追加の基本クラスなし

>>> class FooMeta(type): pass
...
>>> with_metaclass(FooMeta)  # returns a temporary_class object
<class '__main__.temporary_class'>
>>> type(with_metaclass(FooMeta))  # which has a custom metaclass
<class '__main__.metaclass'>
>>> class Foo(with_metaclass(FooMeta)): pass
...
>>> Foo.__mro__  # no extra base classes
(<class '__main__.Foo'>, <type 'object'>)
>>> type(Foo) # correct metaclass
<class '__main__.FooMeta'>
69
Martijn Pieters

[〜#〜] update [〜#〜]six.with_metaclass()関数はその後、デコレータバリアント、つまり@six.add_metaclass()。この更新プログラムは、ベースオブジェクトに関連するいくつかのmroの問題を修正します。新しいデコレータは次のように適用されます。

import six

@six.add_metaclass(Meta)
class MyClass(Base):
    pass

ここにパッチノートがあります および 同様の詳細な例と説明があります 代替デコレータを使用します。

24
pylang