跳转到主要内容

基于BTree的持久化类似字典的对象(常规字典和有序字典),可以用作基类。这是一个相对较重的解决方案,因为每个zc.dict.Dict(以及zc.dict.OrderedDict)至少包含3个持久化对象。如果您打算创建大量此类对象,请记住这一点。要构建,请在此目录中运行`python bootstrap/bootstrap.py`,然后运行`bin/buildout`。强烈推荐使用干净的非系统Python。

项目描述

高效、持久并可继承的字典

如果PersistentDict包含的值超过几个,则效率非常低,而且不建议从BTrees继承。

这个类是对BTree的一个简单包装。它保留了BTree的效率,并且作为基类使用是安全的。此外,它实现了完整的Python字典接口。

>>> from zc.dict import Dict
>>> d = Dict()
>>> d
<zc.dict.dict.Dict object at ...>
>>> d['foo'] = 'bar'
>>> len(d)
1
>>> d['bar'] = 'baz'
>>> len(d)
2

注意,Python字典与这个Dict之间的重要区别在于,Python字典使用哈希,而这个使用BTree比较。实际上,这意味着你的键应该是同质的类型。我们在这些示例中使用字符串。

长度是独立维护的,因为在BTree上len操作效率低下,因为它必须唤醒数据库中的所有桶。

>>> d._len
<BTrees.Length.Length object at ...>
>>> d._len()
2

为了保持小更改的更新效率,我们将它们展开为一系列的setitems。

>>> d.update({'bar': 'moo', 'ding': 'dong', 'beep': 'beep'})
>>> len(d)
4

Dict支持完整的update接口。

>>> d.update([['sha', 'zam'], ['ka', 'pow']])
>>> len(d)
6
>>> d['ka']
'pow'
>>> d.update(left='hook', right='jab')
>>> len(d)
8
>>> d['left']
'hook'

pop需要更新长度。

>>> d.pop('sha')
'zam'
>>> d.pop('ka')
'pow'
>>> d.pop('left')
'hook'
>>> d.pop('right')
'jab'
>>> len(d)
4

……除非它不需要。

>>> d.pop('nonexistent')
Traceback (most recent call last):
...
KeyError: 'nonexistent'
>>> d.pop('nonexistent', 42)
42
>>> len(d)
4

setdefault有时也需要更新长度。

>>> len(d)
4
>>> d.setdefault('newly created', 'value')
'value'
>>> d['newly created']
'value'
>>> len(d)
5
>>> d.setdefault('newly created', 'other')
'value'
>>> d['newly created']
'value'
>>> len(d)
5
>>> del d['newly created'] # set things back to the way they were...

keysvaluesitems返回正常的Python列表。由于底层的BTree,这些总是按照键的排序顺序。

>>> d.keys()
['bar', 'beep', 'ding', 'foo']
>>> d.values()
['moo', 'beep', 'dong', 'bar']
>>> d.items()
[('bar', 'moo'), ('beep', 'beep'), ('ding', 'dong'), ('foo', 'bar')]

然而,通过iter方法可以提供有效的BTree迭代器。

>>> iter(d)
<OO-iterator object at ...>
>>> d.iterkeys()
<OO-iterator object at ...>
>>> d.iteritems()
<OO-iterator object at ...>
>>> d.itervalues()
<OO-iterator object at ...>

popitem从字典中删除并返回一个键值对

>>> len(d)
4
>>> d.popitem()
('bar', 'moo')
>>> len(d)
3

copy方法创建一个Dict的副本

>>> c = d.copy()
>>> c.items() == d.items()
True

然而,由于胆怯,我们不支持比较,除了身份。

>>> c == d
False
>>> Dict() == {}
False
>>> d == d
True

clear从字典中删除所有键

>>> d.clear()
>>> d.keys()
[]
>>> len(d)
0

其余的字典方法委托给底层的BTree

>>> c.has_key('beep')
True
>>> 'BEEP' in c
False
>>> c.get('nonexistent', 'default')
'default'

子类化

为了便于子类化,字典有意具有三个重要特性

  • 所有添加都是通过__setitem__完成的,因此覆盖它将控制添加。

  • 所有删除都是通过popclear完成的,因此覆盖这些方法将控制删除。

  • 如果没有传递参数调用__init__,则不会尝试访问update方法。

让我们用一个简单的子类来演示这些。

>>> class Demo(Dict):
...     def __setitem__(self, key, value):
...         print '__setitem__', key, value
...         super(Demo, self).__setitem__(key, value)
...     def pop(self, key, *args):
...         print 'pop', key, args and arg[0] or '---'
...         return super(Demo, self).pop(key, *args)
...     def update(self, *args, **kwargs):
...         print 'update'
...         super(Demo, self).update(*args, **kwargs)
...     def clear(self):
...         print 'clear'
...         super(Demo, self).clear()
...
>>> demo1 = Demo()
>>> demo2 = Demo([['foo', 'bar'], ['bing', 'baz']], sha='zam')
update
__setitem__ foo bar
__setitem__ bing baz
__setitem__ sha zam
>>> demo2.setdefault('babble')
__setitem__ babble None
>>> del demo2['bing']
pop bing ---
>>> demo2.popitem()
pop babble ---
('babble', None)
>>> demo2.clear()
clear

回归测试

当设置一个已经存在于字典中的项目时,长度不会增加

>>> d.clear()
>>> d['foo'] = 'bar'
>>> d['foo'] = 'baz'
>>> len(d)
1

有序字典:一个保持顺序的持久容器

有序字典提供了大多数Dict的功能,还提供了记住项目添加顺序的功能。它还提供了重新排序项目的API。

重要的是,有序字典当前使用持久列表来存储顺序,这对于大型集合来说表现不佳,因为每次键或顺序发生变化时,整个键集合都必须被序列化。BList将是首选的数据结构,但它尚未发布。

>>> from zc.dict import OrderedDict
>>> d = OrderedDict()
>>> d
<zc.dict.dict.OrderedDict object at ...>
>>> d['foo'] = 'bar'
>>> len(d)
1
>>> d['bar'] = 'baz'
>>> len(d)
2
>>> d['foo']
'bar'
>>> d['bar']
'baz'

键当前是按照添加的顺序排列的。

>>> list(d)
['foo', 'bar']

注意,Python字典与有序字典之间的一个重要区别是,Python字典使用哈希,而这个使用BTree比较。实际上,这意味着你的键应该是同质的类型。我们在这些示例中使用字符串。

长度是独立维护的,因为在BTree上len操作效率低下,因为它必须唤醒数据库中的所有桶。

>>> d._len
<BTrees.Length.Length object at ...>
>>> d._len()
2

为了保持小更改的更新效率,我们将它们展开为一系列的setitems。

>>> d.update({'bar': 'moo', 'ding': 'dong', 'beep': 'beep'})
>>> len(d)
4

注意,在无序的数据结构中对多个新项目进行更新时,结果将按照未定义的顺序将新项目添加到有序字典的末尾。为了设置我们的顺序,我们需要引入一个新的方法:updateOrder。这个方法是一种强制改变顺序的方法:提供一个新的顺序。

>>> list(d) == ['bar', 'beep', 'ding', 'foo']
False
>>> d.updateOrder(('bar', 'beep', 'ding', 'foo'))
>>> d.keys()
['bar', 'beep', 'ding', 'foo']

updateOrder期望的是新顺序中整个键列表

>>> d.updateOrder(['bar', 'beep', 'ding'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> d.updateOrder(['bar', 'beep', 'ding', 'sha', 'foo'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> d.updateOrder(['bar', 'beep', 'ding', 'sha'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> d.updateOrder(['bar', 'beep', 'ding', 'ding'])
Traceback (most recent call last):
...
ValueError: Duplicate keys in order.

Dict支持完整的update接口。如果输入值是有序的,则结果也将是有序的。

>>> d.update([['sha', 'zam'], ['ka', 'pow']])
>>> len(d)
6
>>> d['ka']
'pow'
>>> d.keys()
['bar', 'beep', 'ding', 'foo', 'sha', 'ka']

如果使用关键字参数,则不隐含新项的顺序,但否则它将按预期工作。

>>> d.update(left='hook', right='jab')
>>> len(d)
8
>>> d['left']
'hook'

pop 需要更新长度并保持顺序。

>>> d.pop('sha')
'zam'
>>> d.pop('ka')
'pow'
>>> d.pop('left')
'hook'
>>> d.pop('right')
'jab'
>>> len(d)
4
>>> d.keys()
['bar', 'beep', 'ding', 'foo']

……除非它不需要。

>>> d.pop('nonexistent')
Traceback (most recent call last):
...
KeyError: 'nonexistent'
>>> d.pop('nonexistent', 42)
42
>>> len(d)
4

setdefault有时也需要更新长度。

>>> len(d)
4
>>> d.setdefault('newly created', 'value')
'value'
>>> d['newly created']
'value'
>>> len(d)
5
>>> d.keys()
['bar', 'beep', 'ding', 'foo', 'newly created']
>>> d.setdefault('newly created', 'other')
'value'
>>> d['newly created']
'value'
>>> len(d)
5
>>> del d['newly created'] # set things back to the way they were...

keysvaluesitems返回正常的Python列表。由于底层的BTree,这些总是按照键的排序顺序。

>>> d.keys()
['bar', 'beep', 'ding', 'foo']
>>> d.values()
['moo', 'beep', 'dong', 'bar']
>>> d.items()
[('bar', 'moo'), ('beep', 'beep'), ('ding', 'dong'), ('foo', 'bar')]

然而,通过 iter 方法可以提供高效的迭代器。

>>> iter(d)
<iterator object at ...>
>>> d.iterkeys()
<iterator object at ...>
>>> d.iteritems()
<generator object at ...>
>>> d.itervalues()
<generator object at ...>

popitem 从字典中删除一个项并返回键值对。

>>> len(d)
4
>>> d.popitem()
('bar', 'moo')
>>> len(d)
3

copy方法创建一个Dict的副本

>>> c = d.copy()
>>> c.items() == d.items()
True

然而,由于胆怯,我们不支持比较,除了身份。

>>> c == d
False
>>> OrderedDict() == {}
False
>>> d == d
True

clear从字典中删除所有键

>>> d.clear()
>>> d.keys()
[]
>>> len(d)
0

其余的字典方法委托给底层的BTree

>>> c.has_key('beep')
True
>>> 'BEEP' in c
False
>>> c.get('nonexistent', 'default')
'default'

子类化

为了便于子类化,有序字典具有以下三个重要特性

  • 所有添加都是通过__setitem__完成的,因此覆盖它将控制添加。

  • 所有删除都是通过popclear完成的,因此覆盖这些方法将控制删除。

  • 如果没有传递参数调用__init__,则不会尝试访问update方法。

让我们用一个简单的子类来演示这些。

>>> class Demo(OrderedDict):
...     def __setitem__(self, key, value):
...         print '__setitem__', key, value
...         super(Demo, self).__setitem__(key, value)
...     def pop(self, key, *args):
...         print 'pop', key, args and arg[0] or '---'
...         return super(Demo, self).pop(key, *args)
...     def update(self, *args, **kwargs):
...         print 'update'
...         super(Demo, self).update(*args, **kwargs)
...     def clear(self):
...         print 'clear'
...         super(Demo, self).clear()
...
>>> demo1 = Demo()
>>> demo2 = Demo([['foo', 'bar'], ['bing', 'baz']], sha='zam')
update
__setitem__ foo bar
__setitem__ bing baz
__setitem__ sha zam
>>> demo2.setdefault('babble')
__setitem__ babble None
>>> del demo2['bing']
pop bing ---
>>> demo2.popitem()
pop babble ---
('babble', None)
>>> demo2.clear()
clear

回归测试

当设置一个已经存在于字典中的项目时,长度不会增加

>>> d.clear()
>>> d['foo'] = 'bar'
>>> d['foo'] = 'baz'
>>> len(d)
1

旧版测试

旧数据库可能需要从 zc.dict.ordered 中找到可导入的内容。

>>> from zc.dict.ordered import OrderedDict as Olde
>>> Olde is OrderedDict
True

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源代码发行版

zc.dict-1.2.1.tar.gz (10.8 kB 查看散列)

上传时间 源代码

支持