144

私にはメタクラスを使用するのが好きな友人がいて、定期的にそれらを解決策として提供しています。

メタクラスを使用する必要はほとんどないことを私は心に留めています。なんで?クラスに対してそのようなことをしているのなら、おそらくオブジェクトに対してそれをしているはずだと私は思います。そして、小さな再設計/リファクタリングが必要です。

メタクラスを使用できるようになったため、多くの場所で多くの人々がクラスをある種の二流のオブジェクトとして使用するようになりました。これは私には悲惨なことのように思えます。プログラミングはメタプログラミングに置き換えられますか?残念ながら、クラスデコレータの追加により、さらに受け入れやすくなりました。

ですから、Pythonでのメタクラスの有効な(具体的な)ユースケースを知りたいと思います。または、場合によっては、クラスの変更がオブジェクトの変更よりも優れている理由について理解する必要があります。

始めます:

サードパーティのライブラリを使用する場合、特定の方法でクラスを変更できると便利な場合があります。

(これは私が考えることができる唯一のケースであり、具体的ではありません)

4

21 に答える 21

129

私は最近同じ質問をされ、いくつかの答えを思いつきました。言及されたユースケースのいくつかについて詳しく説明し、いくつかの新しいユースケースを追加したかったので、このスレッドを復活させても問題ないことを願っています.

私が見たほとんどのメタクラスは、次の 2 つのいずれかを行います。

  1. 登録 (データ構造へのクラスの追加):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    をサブクラス化するたびに、クラスが辞書Modelに登録されます。models

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    これは、クラス デコレータでも実行できます。

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    または、明示的な登録関数を使用して:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    実際、これはほとんど同じです。あなたはクラス デコレータについて好ましくないことを述べていますが、それは実際には、クラスで関数を呼び出すための構文糖衣にすぎないため、魔法のようなものはありません。

    とにかく、この場合のメタクラスの利点は継承です。他のソリューションは、明示的に装飾または登録されたサブクラスに対してのみ機能するのに対し、メタクラスはすべてのサブクラスに対して機能するためです。

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. リファクタリング (クラス属性の変更または新しい属性の追加):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    いくつかの属性をサブクラス化Modelして定義するFieldと、その名前が挿入され (たとえば、より有益なエラー メッセージ用)、_fields辞書にグループ化されます (すべてのクラス属性とそのすべての基本クラスを調べなくても簡単に反復できるようにするため)。毎回属性):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    繰り返しますが、これはクラス デコレータを使用して (継承なしで) 実行できます。

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    または明示的に:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    ただし、読み取り可能で保守可能な非メタ プログラミングに対するあなたの主張とは対照的に、これははるかに面倒で、冗長で、エラーが発生しやすいものです。

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

最も一般的で具体的なユースケースを検討した結果、絶対にメタクラスを使用しなければならない唯一のケースは、クラス名または基本クラスのリストを変更する場合です。これらのパラメーターは、定義されるとクラスに組み込まれ、デコレーターがないためです。または関数はそれらをアンベイクできます。

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

これは、似たような名前のクラスや不完全な継承ツリーが定義されているときに警告を発行するフレームワークで役立つかもしれませんが、これらの値を実際に変更するためにトローリングする以外の理由は考えられません. デビッド・ビーズリーならできるかもしれません。

とにかく、Python 3 では、メタクラスにも__prepare__メソッドがあり、クラス本体を 以外のマッピングに評価できるdictため、順序付けられた属性、オーバーロードされた属性、およびその他の邪悪なクールなものがサポートされます。

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

 

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

順序付けられた属性は作成カウンターで実現でき、オーバーロードはデフォルトの引数でシミュレートできると主張するかもしれません。

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

見栄えが悪いだけでなく、柔軟性も低くなります。整数や文字列など、順序付けされたリテラル属性が必要な場合はどうすればよいでしょうか。Noneの有効な値が の場合はどうなりxますか?

最初の問題を解決する創造的な方法を次に示します。

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

そして、2 つ目の問題を解決するための創造的な方法を次に示します。

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

しかし、これは単純なメタクラスよりもはるかにブードゥー的です (特に最初のメタクラスは、本当に脳が溶けてしまいます)。私が言いたいのは、あなたはメタクラスを馴染みがなく、直観に反するものと見なしていますが、プログラミング言語の進化の次のステップと見なすこともできます。考え方を調整する必要があるだけです。結局のところ、関数ポインターを使用して構造体を定義し、それを関数への最初の引数として渡すことを含め、おそらくすべて C で行うことができます。C++ を初めて見た人は、「この魔法は何だろう? コンパイラが暗黙的にパスを渡すのはなぜ?」と言うかもしれません。thisメソッドではなく、通常の静的関数ではありませんか? 議論については明示的かつ詳細に説明する方がよいでしょう」.メタクラスを理解してください。実際には非常に単純なので、便利なときに使用してみませんか?

最後に、メタクラスは素晴らしく、プログラミングは楽しいはずです。標準的なプログラミング構造と設計パターンを常に使用することは、退屈で刺激がなく、想像力を妨げます。少し生きてください!ここにメタメタクラスがあります。

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

編集

これはかなり古い質問ですが、まだ支持を得ているので、より包括的な回答へのリンクを追加すると思いました. メタクラスとその使用法について詳しく知りたい場合は、ここで記事を公開しました。

于 2015-06-25T22:26:32.893 に答える
37

メタクラスの目的は、クラス/オブジェクトの区別をメタクラス/クラスに置き換えることではなく、何らかの方法でクラス定義 (およびそのインスタンス) の動作を変更することです。効果的には、特定のドメインにとってデフォルトよりも役立つ方法でクラスステートメントの動作を変更することです。私がそれらを使用したことは次のとおりです。

  • 通常はハンドラーを登録するためのサブクラスの追跡。これは、いくつかのクラス属性をサブクラス化してセットアップするだけで、特定のもののハンドラーを登録したいプラグイン スタイルのセットアップを使用する場合に便利です。例えば。各クラスがそのタイプに適切なメソッド (再生/タグの取得など) を実装する、さまざまな音楽フォーマットのハンドラーを作成するとします。新しいタイプのハンドラーを追加すると、次のようになります。

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    その後、メタクラスは{'.mp3' : MP3File, ... }etc の辞書を維持し、ファクトリ関数を介してハンドラーを要求すると、適切な型のオブジェクトを構築します。

  • 行動を変える。特定の属性に特別な意味を付けて、それらが存在する場合の動作を変更したい場合があります。たとえば、その名前のメソッドを探して、それらを透過的にプロパティに変換したい場合があり_get_fooます_set_foo。実際の例として、C ライクな構造体定義を提供するために私が書いたレシピを次に示します。メタクラスは、宣言されたアイテムを構造体形式の文字列に変換し、継承などを処理し、それを処理できるクラスを生成するために使用されます。

    他の実際の例については、sqlalchemy のORM やsqlobjectなど、さまざまな ORM を参照してください。繰り返しますが、目的は定義 (ここでは SQL 列定義) を特定の意味で解釈することです。

于 2008-12-24T21:43:05.087 に答える
27

Matplotlib のフロントエンドとして、非インタラクティブなプロットを処理するクラスがあります。ただし、インタラクティブなプロットを実行したい場合があります。いくつかの関数だけで、図の数を増やしたり、ドローを手動で呼び出したりすることができましたが、プロット呼び出しの前後にこれらを行う必要がありました。したがって、インタラクティブなプロット ラッパーとオフスクリーン プロット ラッパーの両方を作成するには、次のような方法よりも、適切なメソッドをラップしてメタクラスを介してこれを行う方が効率的であることがわかりました。

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

このメソッドは API の変更などに対応していませんが、クラス属性を__init__再設定する前にクラス属性を反復処理する方法の方が効率的で、最新の状態に保たれます。

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

もちろん、これを行うためのより良い方法があるかもしれませんが、私はこれが効果的であることを発見しました. もちろん、これは__new__または__init__で行うこともできますが、これが最も簡単な解決策でした。

于 2008-12-26T01:35:56.520 に答える
20

ティム・ピーターの古典的な引用から始めましょう。

メタクラスは、99% のユーザーが心配するよりも深い魔法です。それらが必要かどうか疑問に思っている場合は、必要ありません (実際にそれらを必要としている人は、それらが必要であることを確実に知っており、理由についての説明は必要ありません)。Tim Peters (clp post 2002-12-22)

そうは言っても、私は(定期的に)メタクラスの真の使用法に出くわしました。頭に浮かぶのは、すべてのモデルが models.Model から継承する Django です。models.Model は、DB モデルを Django の ORM の良さでラップするための深刻な魔法を実行します。その魔法は、メタクラスによって発生します。あらゆる種類の例外クラス、マネージャー クラスなどを作成します。

話の始まりについては、django/db/models/base.py のクラス ModelBase() を参照してください。

于 2008-12-25T02:23:47.610 に答える
8

メタクラスを使用する合理的なパターンは、同じクラスがインスタンス化されるたびに繰り返し実行するのではなく、クラスが定義されたときに何かを実行することです。

複数のクラスが同じ特別な動作を共有する場合__metaclass__=X、特別な目的のコードを繰り返したり、アドホックな共有スーパークラスを導入したりするよりも、繰り返す方が明らかに優れています。

しかし、特別なクラスが 1 つしかなく予見可能な拡張がない場合でも 、メタクラスを使用する__new__と、クラス定義本体で__init__特別な目的のコードと通常defclassステートメントを混在させるよりも、クラス変数やその他のグローバル データを初期化するためのクリーンな方法になります。

于 2011-08-14T14:49:02.157 に答える
7

メタクラスは、Python でドメイン固有言語を構築するのに便利です。具体的な例は、 Django 、 SQLObject のデータベーススキーマの宣言構文です。

Ian Bicking によるA Conservative Metaclassの基本的な例:

私が使用したメタクラスは、主に一種の宣言型プログラミング スタイルをサポートするためのものです。たとえば、検証スキーマを考えてみます。

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

その他のテクニック: Python で DSL を構築するための材料(pdf)。

編集(アリによる):コレクションとインスタンスを使用してこれを行う例は、私が好むものです。重要な事実はインスタンスです。これにより、より強力になり、メタクラスを使用する理由がなくなります。さらに、あなたの例ではクラスとインスタンスが混在していることに注意してください。これは、メタクラスだけですべてを実行できないことを示しています。そして、それを行うための真に不均一な方法を作成します。

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

完璧ではありませんが、魔法はほぼゼロで、メタクラスは不要で、均一性が向上しています。

于 2008-12-24T22:54:53.117 に答える
6

私も昨日同じことを考えていて、まったく同感です。私の意見では、コードをより宣言的にしようとする試みによって引き起こされるコードの複雑さは、一般的にコードベースを維持しにくく、読みにくくし、pythonic を少なくします。また、通常、多くの copy.copy()ing (継承を維持し、クラスからインスタンスにコピーするため) が必要であり、何が起こっているかを確認するために多くの場所を調べなければならないことを意味します (常にメタクラスから上を見ます)。パイソン粒も。私は formencode と sqlalchemy のコードを調べて、そのような宣言型のスタイルが価値があるかどうかを確認してきましたが、明らかにそうではありません。このようなスタイルは、記述子 (プロパティやメソッドなど) と不変データに任せる必要があります。Ruby はそのような宣言型スタイルをより適切にサポートしており、コアの Python 言語がその方向に進んでいないことを嬉しく思います。

デバッグに使用されていることがわかります。すべての基本クラスにメタクラスを追加して、より豊富な情報を取得してください。また、(非常に)大規模なプロジェクトでのみ定型コードを削除するために使用されていることもわかります(ただし、明確さは失われます)。たとえば、sqlalchemy はそれらを他の場所で使用し、クラス定義の属性値に基づいて特定のカスタム メソッドをすべてのサブクラスに追加します (例: おもちゃの例)。

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

「hello」に基づく特別なプロパティを持つそのクラスのメソッドを生成するメタクラスを持つことができます (文字列の末尾に「hello」を追加したメソッドなど)。作成するすべてのサブクラスでメソッドを記述する必要がなく、代わりに定義する必要があるのは method_maker_value.

ただし、これが必要になることはめったになく、タイピングを少し減らすだけなので、十分な大きさのコードベースがない限り、検討する価値はありません。

于 2008-12-28T03:20:15.823 に答える
5

Python でメタクラスを使用したのは、Flickr API のラッパーを作成するときだけでした。

私の目標は、flickr の API サイトをスクレイピングし、完全なクラス階層を動的に生成して、Python オブジェクトを使用した API アクセスを可能にすることでした。

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

この例では、Python Flickr API 全体を Web サイトから生成したため、実行時のクラス定義はわかりません。型を動的に生成できることは非常に便利でした。

于 2008-12-24T21:19:39.847 に答える
5

メタクラスはプログラミングに取って代わるものではありません! それらは、一部のタスクを自動化またはよりエレガントにする単なるトリックです。これの良い例は、Pygments構文強調表示ライブラリです。と呼ばれるクラスがRegexLexerあり、ユーザーは一連の字句規則をクラスの正規表現として定義できます。メタクラスは、定義を便利なパーサーに変換するために使用されます。

それらは塩のようなものです。使いすぎは簡単です。

于 2008-12-24T22:15:22.353 に答える
4

メタクラスの唯一の正当なユースケースは、他のせんさく好きな開発者があなたのコードに触れないようにすることです。せんさく好きな開発者がメタクラスをマスターし、あなたのメタクラスをいじり始めたら、それらを防ぐために別のレベルを1つか2つ投入します。それが機能しない場合はtype.__new__、再帰メタクラスを使用するか、おそらくいくつかのスキームを使用し始めてください。

(頬に舌を書いていますが、この種の難読化が行われているのを見ました。Djangoは完璧な例です)

于 2011-03-16T19:13:50.780 に答える
4

GUI ライブラリの中には、複数のスレッドが対話しようとすると問題が発生するものがあります。tkinterはその一例です。イベントとキューを使用して問題を明示的に処理できますが、問題を完全に無視する方法でライブラリを使用する方がはるかに簡単です。見よ -- メタクラスの魔法。

ライブラリ全体をシームレスに動的に書き換えて、マルチスレッド アプリケーションで期待どおりに適切に動作させることができると、状況によっては非常に役立ちます。safetkinterモジュールは、threadboxモジュールによって提供されるメタクラスの助けを借りてそれを行います -- イベントとキューは必要ありません。

の優れた点の 1 つthreadboxは、どのクラスを複製するかを気にしないことです。これは、必要に応じてメタクラスがすべての基本クラスに触れる方法の例を提供します。メタクラスのもう 1 つの利点は、継承クラスでも実行されることです。自分自身を書くプログラム -- なぜでしょうか?

于 2013-06-04T19:29:42.267 に答える
4

変更したいクラスの継承または集約を使用して、必要なことを行うクラスをいつでも構築できるため、絶対にメタクラスを使用する必要はありません。

そうは言っても、Smalltalk と Ruby では既存のクラスを変更できると非常に便利ですが、Python ではそれを直接行うのは好きではありません。

Python でのメタクラス化に関するDeveloperWorksの優れた記事が参考になるかもしれません。ウィキペディアの記事もかなり良いです。

于 2008-12-24T21:17:25.717 に答える
3

私がメタクラスを使用した方法は、クラスにいくつかの属性を提供することでした。たとえば、次のようにします。

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

NameClassを指すようにメタクラスが設定されるすべてのクラスにname属性を配置します。

于 2008-12-25T11:47:43.557 に答える
2

これはマイナーな使い方ですが... メタクラスの便利な点の 1 つは、サブクラスが作成されるたびに関数を呼び出すことです。これを、属性を探すメタクラスにコード化しました__initsubclass__。サブクラスが作成されるたびに、そのメソッドを定義するすべての親クラスが で呼び出され__initsubclass__(cls, subcls)ます。これにより、すべてのサブクラスを何らかのグローバル レジストリに登録し、サブクラスが定義されるたびに不変チェックを実行し、遅延バインディング操作を実行するなどの親クラスを作成できます。関数を手動で呼び出したり、カスタム メタクラスを作成したりする必要はありません。これらの個別の任務をそれぞれ実行します。

気をつけてください、私はゆっくりと、この動作の暗黙の魔法がい​​くぶん望ましくないことに気づきました。クラス定義をコンテキストから外して見ると予想外であるためです...そして、そのソリューションを深刻なこと以外に使用することから遠ざかりました__super各クラスとインスタンスの属性を初期化します。

于 2011-08-14T16:44:08.423 に答える
1

ここで説明されている正当な使用方法があるようです- Rewriting Python Docstrings with a Metaclass.

于 2014-05-25T08:44:58.130 に答える
1

Pydanticは、実行時に型ヒントを適用し、データが無効な場合にユーザー フレンドリーなエラーを提供する、データ検証および設定管理用のライブラリです。BaseModel と数値範囲の検証にメタクラスを使用します。

職場で、クラスによって定義されたいくつかの段階を持つプロセスを持つコードに遭遇しました。これらのステップの順序は、クラスが定義されたときにステップをリストに追加するメタクラスによって制御されていました。これは破棄され、リストに追加することで順序が設定されました。

于 2021-05-18T21:53:01.967 に答える
1

私は最近、メタクラスを使用して、 http://census.ire.org/data/bulkdata.htmlの米国国勢調査データが入力されたデータベース テーブルの周りに SQLAlchemy モデルを宣言的に定義する必要がありました。

IRE は、国勢調査局の p012015、p012016、p012017 などの命名規則に従って整数列を作成する、国勢調査データ テーブル用のデータベース シェルを提供します。

私はmodel_instance.p012017、a) 構文を使用してこれらの列にアクセスできるようにしたい、b) 自分が行っていたことをかなり明確にし、c) モデルで何十ものフィールドを明示的に定義する必要がないようにしたかったので、SQLAlchemy をサブクラス化DeclarativeMetaして、列に対応するモデル フィールドを自動的に作成します。

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

次に、このメタクラスをモデル定義に使用して、モデルで自動的に列挙されたフィールドにアクセスできます。

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...
于 2013-11-08T17:59:56.063 に答える
0

使いやすくするために、バイナリパーサーに一度使用する必要がありました。ネットワーク上に存在するフィールドの属性を使用してメッセージ クラスを定義します。そこから最終的なワイヤ形式を構築するには、宣言された方法で順序付けする必要がありました。順序付けられた名前空間辞書を使用する場合、メタクラスでそれを行うことができます。実際、それはメタクラスの例にあります:

https://docs.python.org/3/reference/datamodel.html#metaclass-example

しかし、一般的には、メタクラスの追加の複雑さが本当に必要な場合は、非常に慎重に評価してください。

于 2016-09-02T12:47:37.237 に答える
0

もう 1 つのユース ケースは、クラス レベルの属性を変更できるようにして、手元のオブジェクトにのみ影響することを確認する場合です。実際には、これはメタクラスとクラスのインスタンス化のフェーズを「マージ」することを意味するため、独自の (一意の) 種類のクラス インスタンスのみを処理することになります。

また、(読みやすさポリモーフィズムの懸念から)クラスレベルでのみ実行できる(頻繁に変更される)インスタンスレベルの属性に基づく計算から返される値(可能性がある)を動的に定義 したい場合にも、それを行う必要がありました。つまり、メタクラスのインスタンス化の後、クラスのインスタンス化の前です。property

于 2019-08-17T20:53:15.887 に答える