7

type特殊な型を作成できるクラスを作成するために、サブクラス化しようとしています。例ListType

>>> ListOfInt = ListType(list, value_type=int)
>>> issubclass(ListOfInt, list)
True
>>> issubclass(list, ListOfInt)
False
>>> # And so on ...

ただし、これListOfIntがインスタンスの作成に使用されることはありません。他のタイプと比較するために操作できるインスタンスとして使用するだけtypeです...特に、私の場合、入力のタイプに応じて適切な操作を検索する必要があり、次のタイプが必要です。より多くの精度が含まれています(list of intまたはXML stringなど)。

だからここに私が思いついたものがあります:

class SpzType(type):

    __metaclass__ = abc.ABCMeta

    @classmethod
    def __subclasshook__(cls, C):
        return NotImplemented

    def __new__(cls, base, **features):
        name = 'SpzOf%s' % base.__name__
        bases = (base,)
        attrs = {}
        return super(SpzType, cls).__new__(cls, name, bases, attrs)

    def __init__(self, base, **features):
        for name, value in features.items():
            setattr(self, name, value)

上記のコードでは、の使用法はabc明らかではありません...ただし、上の例のようにサブクラスを記述したい場合はListType、便利になります...

基本的な機能は実際に機能します:

>>> class SimpleType(SpzType): pass
>>> t = SimpleType(int)
>>> issubclass(t, int)
True
>>> issubclass(int, t)
False

しかし、tがのインスタンスであるかどうかを確認しようとするとSpzType、Pythonがおかしくなります:

>>> isinstance(t, SpzType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)

何が起こっているのかを調べたpdb.pm()ところ、次のコードでエラーが発生することがわかりました。

>>> SpzType.__subclasscheck__(SimpleType)
TypeError: __subclasscheck__() takes exactly one argument (0 given)

変 ?!明らかに議論があります...それで、それはどういう意味ですか?何か案が ?誤用しましたabcか?

4

4 に答える 4

1

kindallからのコメントのおかげで、コードを次のようにリファクタリングしました。

class SpzType(abc.ABCMeta):

    def __subclasshook__(self, C):
        return NotImplemented

    def __new__(cls, base, **features):
        name = 'SpzOf%s' % base.__name__
        bases = (base,)
        attrs = {}
        new_spz = super(SpzType, cls).__new__(cls, name, bases, attrs)
        new_spz.__subclasshook__ = classmethod(cls.__subclasshook__)
        return new_spz

    def __init__(self, base, **features):
        for name, value in features.items():
            setattr(self, name, value)

つまり、基本的にSpzTypeは、のサブクラスにabc.ABCMetaなり、subclasshookはインスタンスメソッドとして実装されます。それはうまく機能し、(IMO)エレガントです!!!

編集:トリッキーなことがありました...__subclasshook__クラスメソッドである必要があるため、手動でクラスメソッド関数を呼び出す必要があります...そうでない場合、実装したい場合は機能しません__subclasshook__

于 2011-06-13T20:36:29.170 に答える
1

やりたいことは、次のようなクラス ファクトリ関数を使用すると、おそらくより簡単に実行できます。少なくとも私にとっては、操作しようとしているさまざまなレベルをまっすぐに保つことがより簡単になります.

def listOf(base, types={}, **features):
    key = (base,) + tuple(features.items())
    if key in types:
        return types[key]
    else:

        if not isinstance(base, type):
            raise TypeError("require element type, got '%s'" % base)

        class C(list):

             def __init__(self, iterable=[]):
                 for item in iterable:
                     try:    # try to convert to desired type
                         self.append(self._base(item))
                     except ValueError:
                         raise TypeError("value '%s' not convertible to %s"
                            % (item, self._base.__name__))

              # similar methods to type-check other list mutations

        C.__name__ = "listOf(%s)" % base.__name__
        C._base = base
        C.__dict__.update(features)  
        types[key] = C
        return C

dictここでa をキャッシュとして使用していることに注意してください。これにより、要素の型と機能の特定の組み合わせに対して同じクラス オブジェクトを取得できます。これにより、listOf(int) is listOf(int)常にTrue.

于 2011-06-13T20:22:40.180 に答える
1

あなたが何を達成したいのかよくわかりません。直接使うよりもcollectionsモジュールを使ったほうがいいのではないでしょうか?abc

PEP 3119にジェネリック コレクション クラスに関する詳細情報があります。

于 2011-06-13T16:14:20.780 に答える
1

これは、どのクラスでも機能する私の他の回答のデコレータバージョンです。デコレータは、目的の属性を持つ元のクラスのサブクラスを返すファクトリ関数を返します。このアプローチの良い点は、メタクラスを強制しないことです。そのため、ABCMeta必要に応じてメタクラス (例: ) を競合なしで使用できます。

また、基本クラスがメタクラスを使用する場合、そのメタクラスを使用して、生成されたサブクラスがインスタンス化されることにも注意してください。必要に応じて、目的のメタクラスをハードコーディングするか、メタクラスをテンプレート クラスのデコレーターにするデコレーターを作成することもできます。

__classinit__()存在する場合、ファクトリに渡された引数がクラス メソッドに渡されるため、クラス自体に引数を検証してその属性を設定するコードを含めることができます。(これは、メタクラスの の後に呼び出されます__init__()。)__classinit__()クラスを返す場合、このクラスは生成されたクラスの代わりにファクトリによって返されるため、この方法で生成手順を拡張することもできます (たとえば、型チェックされたリスト クラスの場合、項目を要素型に強制する必要があるかどうかに応じて、2 つの内部クラスのいずれかを返すことができます)。

__classinit__()が存在しない場合、ファクトリに渡される引数は、単に新しいクラスのクラス属性として設定されます。

型制限のあるコンテナー クラスを作成しやすいように、要素の型を機能辞書とは別に処理しました。渡されない場合は無視されます。

以前と同様に、ファクトリによって生成されたクラスはキャッシュされるため、同じ機能を持つクラスを呼び出すたびに、同じクラス オブジェクト インスタンスが取得されます。

def template_class(cls, classcache={}):

    def factory(element_type=None, **features):

        key = (cls, element_type) + tuple(features.items())
        if key in classcache:
            return classcache[key]

        newname  = cls.__name__
        if element_type or features:
            newname += "("
            if element_type:
                newname += element_type.__name__
                if features:
                    newname += ", "
            newname += ", ".join(key + "=" + repr(value)
                                 for key, value in features.items())
            newname += ")"

        newclass = type(cls)(newname, (cls,), {})
        if hasattr(newclass, "__classinit__"):
            classinit = getattr(cls.__classinit__, "im_func", cls.__classinit__)
            newclass = classinit(newclass, element_type, features) or newclass
        else:
            if element_type:
                newclass.element_type = element_type
            for key, value in features.items():
                setattr(newclass, key, value)

        classcache[key] = newclass
        return newclass

    factory.__name__ = cls.__name__
    return factory

型制限 (実際には型変換) リスト クラスの例:

@template_class
class ListOf(list):

    def __classinit__(cls, element_type, features):
        if isinstance(element_type, type):
            cls.element_type = element_type
        else:
            raise TypeError("need element type")

    def __init__(self, iterable):
        for item in iterable:
            try:
                self.append(self.element_type(item))
            except ValueError:
                raise TypeError("value '%s' not convertible to %s"
                        % (item, self.element_type.__name__))

    # etc., to provide type conversion for items added to list 

新しいクラスの生成:

Floatlist = ListOf(float)
Intlist   = ListOf(int)

次にインスタンス化します。

print FloatList((1, 2, 3))       # 1.0, 2.0, 3.0
print IntList((1.0, 2.5, 3.14))  # 1, 2, 3

または、クラスを作成して 1 つのステップでインスタンス化するだけです。

print ListOf(float)((1, 2, 3))
print ListOf(int)((1.0, 2.5, 3.14))
于 2011-06-15T15:59:50.310 に答える