我有以下代码:
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”的具体子类并创建它。
这是有效的方法,还是我误解和做错了什么?
答案 0 :(得分:9)
我会用装饰师做这件事。此外,存储实体 - &gt;字典中的子类映射允许您使用字典查找替换线性扫描。
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)
装饰者更有魅力,让我们对自己感到高兴和喜欢,但字典很简单,有用而且非常重要。这很容易理解,也很难出错。除非你真的需要做一些神奇的事情,否则我认为这就是你要走的路。无论如何,你为什么要这样做?
答案 1 :(得分:5)
我认为这是您想要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_
属性而不是__entity__
。答案 2 :(得分: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'>
顺便说一句,工厂实例化类,因此该名称不适合仅返回类的函数。
答案 3 :(得分:-1)
这种有效的方法,或者我可能是一些误解和做错的事情?
有效。所以在某种意义上它是“有效的”。
完全浪费代码。所以在某种意义上它不是“有效的”。
此类构造没有任何用例。现在您已经构建了它,您可以继续解决实际问题。