2

私は次のコードを持っています:

class EntityBase (object) :
    __entity__ = None

    def __init__ (self) :
        pass

def entity (name) :
    class Entity (EntityBase) :
        __entity__ = name

        def __init__ (self) :
            pass

    return Entity

class Smth (entity ("SMTH")) :
    def __init__ (self, a, b) :
        self.a = a
        self.b = b

# added after few comments -->
def factory (tag) :
    for entity in EntityBase.__subclasses__ () :
        if entity.__entity__ == tag :
            return entity.__subclasses__ ()[0]

    raise FactoryError (tag, "Unknown entity")

s = factory ("SMTH") (1, 2)
print (s.a, s.b)
# <--

これで、ファクトリでEntityBaseのすべてのサブクラスを取得し、「SMTH」の具体的なサブクラスを見つけて作成できます。

これは有効なアプローチですか、それとも私は何かを誤解して間違っているのでしょうか?

4

4 に答える 4

9

これはデコレータで行います。また、エンティティ->サブクラスマップを辞書に保存すると、線形スキャンをdictルックアップに置き換えることができます。

class EntityBase(object):
    _entity_ = None
    _entities_ = {}

    @classmethod
    def factory(cls, entity):
        try:
            return cls._entities_[entity]
        except KeyError:
            raise FactoryError(tag, "Unknown entity")

    @classmethod
    def register(cls, entity):
        def decorator(subclass):
            cls._entities_[entity] = subclass
            subclass._entity_ = entity
            return subclass
        return decorator

 factory = EntityBase.factory
 register = EntityBase.register

 @register('Smith')
 class Smith(EntityBase):
     def __init__(self, a, b):
         self.a = a
         self.b = b

 s = factory('Smith')(1, 2)

__entity__線形スキャンを実装するためだけに属性を使用していたのかどうか、その属性が実際に役立つかどうかはわかりません。私はそれを残しましたが、あなたがそれを取り出した場合、エンティティに関連付けられたクラスはから継承する必要さえなくEntityBase、あなたはそれをのような名前に変更することができますRegistry。これにより、継承ツリーが浅くなり、共通祖先を介して関連していないクラスで使用できるようになります。

ユースケースによっては、それを行うためのより良い方法は

factory = {}

class Smith(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
factory['Smith'] = Smith

class Jones(object):
    def __init__(self, c, d):
         self.c = c
         self.d = d
factory['Jones'] = Jones

s = factory['Smith'](1, 2)
j = factory['Jones'](3, 4)

デコレータはより魅力的で、自分自身について素晴らしく空想を感じさせましょう。しかし、辞書はわかりやすく、便利で、要領を得ています。理解しやすく、間違えにくいです。あなたが本当に何か魔法をする必要がない限り、私はそれが進むべき道だと思います。とにかく、なぜあなたはこれをしたいのですか?

于 2010-09-24T12:25:35.703 に答える
5

これは、Pythonメタクラスが必要な数少ないケースの1つだと思います。

class Entity(object):
    class __metaclass__(type):
        ENTITIES = {}

        def __new__(mcs, name, bases, cdict):
            cls = type.__new__(mcs, name, bases, cdict)
            try:
                entity = cdict['_entity_']
                mcs.ENTITIES[entity] = cls
            except KeyError:
                pass
            return cls

    @classmethod
    def factory(cls, name):
        return cls.__metaclass__.ENTITIES[name]

class Smth(Entity):
    _entity_ = 'SMTH'

    def __init__(self, a, b):
        self.a = a
        self.b = b

s = Entity.factory("SMTH")(1, 2)
print (s.a, s.b)

コードとの微妙な違いは次のとおりです。

  • entity()ファクトリ関数を使用してサブクラスを作成してから、そのサブクラスをサブクラス化する必要はありません。このアプローチでは、必要以上に多くのサブクラスが作成されるだけでなくEntityBase.__subclasses__()、クラスが含まれていないためにコードが機能しなくなりますSmth
  • で始まる識別子と終わる識別子__はPython用に予約されているため、の_entity_代わりに属性を使用しています__entity__
于 2010-09-24T12:39:56.427 に答える
3

メタクラスは、定義されたクラスを追跡できます。Register.__init__このメタクラスを持つクラスが定義されたときに呼び出されます。名前とオブジェクトをメタクラスのレジストリ辞書に追加するだけです。このようにして、後で直接調べることができます。

registry = {} # dict of subclasses

def get_entity( name ):
    return registry[name]    

class Register(type):
    def __init__(cls, name, bases, dict):
        registry[name] = cls
        type.__init__(cls,name, bases, dict)

class EntityBase(object):
    __metaclass__ = Register

class OneThing(EntityBase):
    pass

class OtherThing(OneThing):
    pass

print registry # dict with Entitybase, OneThing, OtherThing
print get_entity("OtherThing") # <class '__main__.OtherThing'>

ところで、ファクトリはクラスをインスタンス化するため、その名前はクラスを返すだけの関数には適していません。

于 2010-09-24T13:03:49.000 に答える
-1

この有効なアプローチ、または多分私は何かを誤解して間違ったことをしていますか?

できます。したがって、ある意味では「有効」です。

それはコードの完全な無駄です。したがって、ある意味では「有効」ではありません。

この種の構成のユースケースはありません。作成したので、実際の問題の解決に進むことができます。

于 2010-09-24T12:18:36.460 に答える