如何在保持顺序的同时收集类变量



我正在尝试创建类似SQLAlchemy declarative_base的模式类。模式类应该使用继承进行扩展。这是我到目前为止的尝试。除了属性(类变量)按字母顺序排序之外,它工作得很好。

import inspect
class BaseProp(object):
    def __init__(self, name):
        self.name = name
class StringProp(BaseProp): pass
class EmailProp(BaseProp): pass
class BaseSchema(object):
    @classmethod
    def _collect_props(cls):
        prop_filter =  lambda a:not(inspect.isroutine(a)) and issubclass(a.__class__, BaseProp)
        return inspect.getmembers(cls, prop_filter)
    @classmethod
    def as_list(cls):
        return cls._collect_props()
    @classmethod
    def as_dict(cls):
        return dict(cls._collect_props())
class UserIdentSchema(BaseSchema):
    email = EmailProp('email')
    password = StringProp('password')
class UserProfileSchema(UserIdentSchema):
    name = StringProp('name')
    phone = StringProp('phone')
from pprint import pprint
pprint(UserProfileSchema.as_list())

结果如下。注意,属性是按字母顺序排序的。

[('email', <__main__.EmailProp object at 0x10518a950>),
 ('name', <__main__.StringProp object at 0x10517d910>),
 ('password', <__main__.StringProp object at 0x10517d8d0>),
 ('phone', <__main__.StringProp object at 0x10517d950>)]

我想在顶部有基本模式的道具,然后从子类的道具。实现这一目标的最佳方式是什么?我必须经过AST…吗?

编辑:我需要保持一个类中的属性的顺序,以及

这是一个可行的解决方案——我不保证这是具有生产价值的代码……例:我没有想太多会发生什么,如果你试图混合在另一个(非BaseSchema)类与BaseSchema子类。我认为可能有效,但您需要尝试一下才能看到…

import ast
import inspect
from collections import OrderedDict
class _NodeTagger(ast.NodeVisitor):
    def __init__(self):
        self.class_attribute_names = {}
    def visit_Assign(self, node):
        for target in node.targets:
            self.class_attribute_names[target.id] = target.lineno
    # Don't visit Assign nodes inside Function Definitions.
    def visit_FunctionDef(self, unused_node):
        return None

class BaseProp(object):
    def __init__(self, name):
        self.name = name

class StringProp(BaseProp): pass

class EmailProp(BaseProp): pass

class _BaseSchemaType(type):
    def __init__(cls, name, bases, dct):
        cls._properties = OrderedDict()
        for b in bases:
          # if the object has a metaclass which is this
          # class (or subclass of this class...)
          # then we add it's properties to our own.
          if issubclass(type(b), _BaseSchemaType):
            cls._properties.update(b._properties)
        # Finally, we add our own properties.  We find our own source code
        # read it and tag the nodes where we find assignment.
        source = inspect.getsource(cls)
        nt = _NodeTagger()
        nt.visit(ast.parse(source))
        attr_names = nt.class_attribute_names
        properties = {n: prop for n, prop in dct.items()
                      if isinstance(prop, BaseProp)}
        sorted_attrs = sorted(properties.items(),
                             key=lambda item: attr_names[item[0]])
        cls._properties.update(sorted_attrs)
    # methods on a metaclass are basically just classmethods ...
    def as_list(cls):
      return list(cls._properties.items())
    def as_dict(cls):
      return cls._properties.copy()

class BaseSchema(object):
    __metaclass__ = _BaseSchemaType
class UserIdentSchema(BaseSchema):
    email = EmailProp('email')
    password = StringProp('password')
class UserProfileSchema(UserIdentSchema):
    name = StringProp('name')
    phone = StringProp('phone')
from pprint import pprint
pprint(UserProfileSchema.as_list())

关于元类的问题很抱歉——但是它确实使这里的事情变得更容易。现在,当类被导入时,所有神奇的事情都发生了——每次调用as_list时都不会出现混乱的自省。它还使我能够快速访问类的属性和它的基础,否则我需要从__dict____bases__中取出(或从inspect中找出)。

相关内容

  • 没有找到相关文章

最新更新