<p>我们可以用元类shenanegans来做这个。。。你知道吗</p>
<p>在Python2中,属性在dict中传递给元类,而不是
为了保持顺序,我们还需要一个基类来处理
区分应映射到行中的类属性。在python3中,我们可以省去几乎所有的基描述符类。你知道吗</p>
<pre><code>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
</code></pre>
<p>我们需要一个python描述符来描述我们希望映射到
行。slot是一种很好的获取数据描述符的方法,不需要做很多工作。一个
不过,需要注意的是,我们必须手动删除helper实例才能使
实插槽描述符可见。你知道吗</p>
<pre><code>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))
</code></pre>
<p>对于计算字段,我们可以记忆结果。注释的回忆录
如果没有内存泄漏,实例很棘手,我们需要weakref。或者,我们
可以安排另一个插槽来存储缓存的值。这也不是很线程安全,但非常接近。你知道吗</p>
<pre><code>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))
</code></pre>
<p>在元类上,我们在上面创建的所有描述符都是按
创建顺序,并指示对新创建的类进行注释。确实如此
不能正确处理派生类,并且可以使用其他一些便利,如
所有插槽的<code>__init__</code>。你知道吗</p>
<pre><code>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
</code></pre>
<p>最后,我们希望从中继承一个基类,这样就可以有一个<code>to_row</code>
方法。这只会调用所有<code>__get__</code>的
描述符,按顺序。你知道吗</p>
<pre><code>class DryBase(object):
__metaclass__ = DryMeta
def to_row(self):
cls = type(self)
return [desc.__get__(self, cls) for desc in cls._header_descriptors]
</code></pre>
<p>假设所有这些都隐藏起来,看不见,一个类的定义
使用此功能的用户基本上不需要重复。唯一的捷径就是
为了实用,每个字段都需要一个python友好的名称,因此我们有了
<code>alias</code>键将<code>'seeds?'</code>关联到<code>has_seeds</code></p>
<pre><code>class ADryRow(DryBase):
__slots__ = ['seeds']
ripeness = slot()
colour = slot()
juiciness = slot()
@memo(alias='seeds?')
def has_seeds(self):
print "Expensive!!!"
return self.seeds > 0
</code></pre>
<pre><code>>>> 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]
</code></pre>