Python按标识符反向查找类

时间:2016-01-26 01:53:37

标签: python identifier

我有几个"型号"可以由具有不同标识符的客户端实例化的类(ModelFoo,ModelBar,ModelBaz,ModelQux)。为了将标识符映射到正确的模型类,我创建了一个映射类:

class ModelTypes(object):
  """ Enumeration of supported class types, mapping userland identifier to constructor.
  """
  # Multiple names per model type enable us to run several instances in parallel
  FooModel = ModelFoo
  fooMod = ModelFoo
  foo = Foo
  ff = Foo

  Bar = ModelBar
  bar = ModelBar

  Baz = ModelBaz
  baz = ModelBaz

  Qux = ModelQux
  qux = ModelQux


  @classmethod
  def getTypes(cls):
    """ Return the names of the attributes explicitly defined above.
    """
    for attrName in dir(cls):
      attrValue = getattr(cls, attrName)
      if (isinstance(attrValue, type) and
          issubclass(attrValue, acceptableClassImplementations)):
        yield attrName # attrName is an acceptable model name

也就是说,我使用ModelTypes类进行反向查找,我需要将简单标识符(例如qux)映射回相应的类(ModelQux)。但是添加客户端可能使用的所有标识符变得繁琐。是否有标准/建议的方法将关键字映射到类?

1 个答案:

答案 0 :(得分:0)

如果您想要的只是更少写入以将多个变量定义为相同的值,则有一种语法:

x = y = z = 0
print(x,y,z)

所以你可以这样做:

  FooModel = fooModel = ModelFoo
  foo = ff = Foo

  Bar = bar = ModelBar
  Baz = baz = ModelBaz
  Qux = quz = ModelQux

但是如果你想要更自动化的东西,那么你想要一个dict 唯一的内置映射对象,但是它可以被实现为以与项目相同的方式响应属性:

class ModelTypes(dict):
    __slots__ = ()#otherwise each object would have a second dict used for attributes

    #this will mean d.x acts the same as d['x']
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
    #would you still need this or would you just use .keys() instead?
    def getTypes(self): #maybe take an optional argument to use in issubclass()?
        return [name for name,cls in self.items() \
                 if (isinstance(cls,type) and 
                     issubclass(cls, acceptableClassImplementations))]

同样dict的任何子类都可以定义一个__missing__方法,如果你试图得到一个不存在的密钥就会被调用,所以你可以实现它再次检查dict忽略大小写并检查类的文档名称:

    def __missing__(self,key):
        #if the user uses different case this will check again with generic case
        #also checks against name attribute of types
        if not isinstance(key,str) or key=="":
            raise KeyError(key)

        key = key.lower()
        for name,cls in self.items():
            if key == name.lower()):
                return cls
            if key == getattr(cls,"__name__","").lower():
                return cls
        raise KeyError(key)

然后您可以定义一个对应于一个类的名称,并且将处理所有变体:

class FooModel:pass
class FooBar:pass
class ModelQux:pass

#other then added commas this is just as clean as inside a class
models = ModelTypes(foo=FooModel,
                    bar=FooBar,
                    qux=ModelQux)

print(models.foo is models["foo"])   #.foo works from overriding __getattr__
print(models.foo is models.Foo)      #'Foo' works because __missing__ checked it again with lower case
print(models.foo is models.FooModel) #'FooModel' works because __missing__ also checks class names
print(models.foo is models.foomodel) #it checks the .lower() of key against .lower() of class name