动态基类和工厂

发布于 2024-09-24 22:24:06 字数 756 浏览 7 评论 0原文

我有以下代码:

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

这是有效的方法还是我可能误解并做错了什么?

I have following code:

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)
# <--

Now in factory I can get all subclasses of EntityBase, find concrete subclass for "SMTH" and create it.

Is this valid approach or maybe I something misunderstood and doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

热情消退 2024-10-01 22:24:06

我会用装饰器来做这件事。另外,存储实体->字典中的子类映射允许您用字典查找替换线性扫描。

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)

装饰器更精美,让我们对自己感觉良好,但字典很简单,有用且切中要害。它很容易理解,也很难出错。除非你真的需要做一些神奇的事情,那么我认为这就是正确的方法。无论如何,你为什么要这样做?

I would do this with a decorator. Also, storing the entity -> subclass map in a dictionary lets you replace a linear scan with a dict lookup.

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)

I'm not sure if the __entity__ attribute is actually useful to you of if you were just using it to implement the linear scan. I left it in but if you took it out, then the classes associated with entity wouldn't even need to inherit from EntityBase and you could rename it to something like Registry. This shallows up your inheritance tree and opens the possibility of using on classes that aren't related through common descent.

Depending on what your use case is, a better way to do it might just be

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)

The decorator is fancier and let's us feel nice and fancy about ourselves but the dictionary is plain, useful and to the point. It is easy to understand and difficult to get wrong. Unless you really need to do something magic, then I think that that's the way to go. Why do you want to do this, anyway?

公布 2024-10-01 22:24:06

我认为这是您需要 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)

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

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

I think this is one of the few cases where you want a 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)

A few more subtle differences from your code:

  • There's no need to create a subclass with your entity() factory function then subclass that subclass. That approach not only creates more subclasses than necessary, but also makes your code not work because EntityBase.__subclasses__() doesn't contain the Smth class.
  • Identifiers which both begin and end with __ are reserved for Python, so I'm using the _entity_ attribute instead of __entity__.
心房敞 2024-10-01 22:24:06

元类可以跟踪定义的类。当定义具有此元类的类时,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'>

顺便说一句,工厂实例化类,因此该名称不适合仅返回类的函数。

A metaclass can keep track of the defined classes. Register.__init__ is called when a class with this metaclass is defined. We can just add the name and object to a registry dict in the metaclass. This way you can look it up directly later.

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

Btw, a factory instantiates classes, so the name is not fitting for a function that only returns a class.

命比纸薄 2024-10-01 22:24:06

这种有效的方法或者也许我误解了并且做错了什么?

有用。所以从某种意义上说它是“有效的”。

这完全是浪费代码。所以从某种意义上说,它不是“有效的”。

这种构造没有任何用例。现在您已经构建了它,您可以继续解决实际问题。

this valid approach or maybe I something misunderstood and doing wrong?

It works. So in one sense it's "valid".

It's a complete waste of code. So in one sense it's not "valid".

There aren't any use cases for this kind of construct. Now that you've built it, you can move on to solving practical problems.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文