动态基类与工厂

2024-09-27 17:46:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下代码:

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”的具体子类并创建它。在

这是有效的方法还是我被误解和做错了什么?在


Tags: nameselfreturninitfactorydeftagpass
3条回答

我会找个装修工来做。另外,在字典中存储entity->;子类映射可以让您用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。这会使继承树变浅,并打开了在通过公共继承没有关联的类上使用的可能性。在

根据您的用例是什么,更好的方法可能是

^{pr2}$

让我们觉得字典是有用的,但我们自己觉得这本字典是好的,更花哨。这很容易理解,很难出错。除非你真的需要做些魔术,否则我认为那是正确的方法。你为什么要这么做?在

我想这是您想要Python metaclass的少数几种情况之一:

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_属性而不是{}。在

元类可以跟踪定义的类。Register.__init__在具有此元类的类被定义时调用。我们只需将名称和对象添加到元类中的注册表dict中。这样你以后就可以直接查了。在

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'>

顺便说一句,工厂实例化了类,所以这个名称不适合只返回类的函数。在

相关问题 更多 >

    热门问题