动态基类和工厂

W55*_*4xv 2 python factory factory-pattern

我有以下代码:

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)
# <--
Run Code Online (Sandbox Code Playgroud)

现在在工厂中我可以获得EntityBase的所有子类,找到"SMTH"的具体子类并创建它.

这是有效的方法,还是我误解和做错的事情?

aar*_*ing 9

我会用装饰师做这件事.此外,将实体 - >子类映射存储在字典中可让您使用字典查找替换线性扫描.

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)
Run Code Online (Sandbox Code Playgroud)

__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)
Run Code Online (Sandbox Code Playgroud)

装饰者更加漂亮,让我们对自己感到高兴和喜欢,但字典很简单,有用而且非常重要.这很容易理解,也很难出错.除非你真的需要做一些神奇的事情,否则我认为这就是要走的路.无论如何,你为什么要这样做?


lla*_*ram 5

我认为这是您想要Python 元类的少数情况之一:

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)
Run Code Online (Sandbox Code Playgroud)

与您的代码有一些细微差别:

  • 没有必要使用entity()工厂函数创建子类,然后子类化该子类.这种方法不仅创建了比必要更多的子类,而且还使您的代码无法工作,因为EntityBase.__subclasses__()它不包含Smth类.
  • 开头和结尾的标识符__都是为Python保留的,所以我使用的是_entity_属性而不是__entity__.