我们可以使用一些元类恶作剧来做到这一点......
在 python 2 中,属性在字典中传递给元类,不保留顺序,我们还需要一个基类,以便我们可以区分应该映射到行中的类属性。在 python3 中,我们可以省去几乎所有这些基本描述符类。
import itertools
import functools
@functools.total_ordering
class DryDescriptor(object):
    _order_gen = itertools.count()
    def __init__(self, alias=None):
        self.alias = alias
        self.order = next(self._order_gen)
    def __lt__(self, other):
        return self.order < other.order
对于我们希望映射到行中的每个属性,我们都需要一个 Python 描述符。插槽是一种无需太多工作即可获取数据描述符的好方法。不过需要注意的是,我们必须手动删除帮助程序实例以使真正的插槽描述符可见。
class slot(DryDescriptor):
    def annotate(self, attr, attrs):
        del attrs[attr]
        self.attr = attr
        slots = attrs.setdefault('__slots__', []).append(attr)
    def annotate_class(self, cls):
        if self.alias is not None:
            setattr(cls, self.alias, getattr(self.attr))
对于计算域,我们可以记忆结果。在没有内存泄漏的情况下,从带注释的实例中记忆是很棘手的,我们需要弱引用。或者,我们可以安排另一个插槽来存储缓存值。这也不是线程安全的,但非常接近。
import weakref
class memo(DryDescriptor):
    _memo = None
    def __call__(self, method):
        self.getter = method
        return self
    def annotate(self, attr, attrs):
        if self.alias is not None:
            attrs[self.alias] = self
    def annotate_class(self, cls): pass
    def __get__(self, instance, owner):
        if instance is None:
            return self
        if self._memo is None:
            self._memo = weakref.WeakKeyDictionary()
        try:
            return self._memo[instance]
        except KeyError:
            return self._memo.setdefault(instance, self.getter(instance))
在元类上,找到我们上面创建的所有描述符,按创建顺序排序,并指示对新创建的类进行注释。这不能正确处理派生类,并且可以使用其他一些便利,例如__init__所有插槽。
class DryMeta(type):
    def __new__(mcls, name, bases, attrs):
        descriptors = sorted((value, key) 
                             for key, value 
                             in attrs.iteritems() 
                             if isinstance(value, DryDescriptor))
        for descriptor, attr in descriptors:
            descriptor.annotate(attr, attrs)
        cls = type.__new__(mcls, name, bases, attrs)
        for descriptor, attr in descriptors:
            descriptor.annotate_class(cls)
        cls._header_descriptors = [getattr(cls, attr) for descriptor, attr in descriptors]
        return cls
最后,我们希望继承一个基类,以便我们可以拥有一个to_row
方法。这只是按顺序调用所有__get__相应描述符的所有 s。
class DryBase(object):
    __metaclass__ = DryMeta
    def to_row(self):
        cls = type(self)
        return [desc.__get__(self, cls) for desc in cls._header_descriptors]
假设所有这些都隐藏起来,看不到,使用此功能的类的定义几乎没有重复。唯一的缺点是为了实用,每个字段都需要一个 python 友好的名称,因此我们有
alias关联的'seeds?'键has_seeds
class ADryRow(DryBase):
    __slots__ = ['seeds']
    ripeness = slot()
    colour = slot()
    juiciness = slot()
    @memo(alias='seeds?')
    def has_seeds(self):
        print "Expensive!!!"
        return self.seeds > 0
        
>>> my_row = ADryRow()
>>> my_row.ripeness = "tart"
>>> my_row.colour = "#8C2"
>>> my_row.juiciness = 0.3479
>>> my_row.seeds = 19
>>>
>>> print my_row.to_row()
Expensive!!!
['tart', '#8C2', 0.3479, True]
>>> print my_row.to_row()
['tart', '#8C2', 0.3479, True]