跳转到主要内容

超级快速、高效存储的 Python Trie。

项目描述

datrie travis appveyor

超级快速、高效存储的 Python Trie (2.x 和 3.x)。使用 libdatrie

安装

pip install datrie

用法

创建一个能够存储具有小写 ascii 键的项的新 trie

>>> import string
>>> import datrie
>>> trie = datrie.Trie(string.ascii_lowercase)

trie 变量是一个类似于字典的对象,它可以具有特定范围内的 Unicode 键和 Python 对象作为值。

除了实现映射接口之外,tries还简化了查找给定前缀的项,反之亦然,查找键为给定字符串前缀的项。作为常见的特殊情况,也支持查找最长的前缀项。

向其中添加一些值(datrie的键必须是unicode;以下示例针对Python 2.x)

>>> trie[u'foo'] = 5
>>> trie[u'foobar'] = 10
>>> trie[u'bar'] = 'bar value'
>>> trie.setdefault(u'foobar', 15)
10

检查u'foo'是否在trie中

>>> u'foo' in trie
True

获取一个值

>>> trie[u'foo']
5

查找一个单词的所有前缀

>>> trie.prefixes(u'foobarbaz')
[u'foo', u'foobar']

>>> trie.prefix_items(u'foobarbaz')
[(u'foo', 5), (u'foobar', 10)]

>>> trie.iter_prefixes(u'foobarbaz')
<generator object ...>

>>> trie.iter_prefix_items(u'foobarbaz')
<generator object ...>

查找一个单词的最长前缀

>>> trie.longest_prefix(u'foo')
u'foo'

>>> trie.longest_prefix(u'foobarbaz')
u'foobar'

>>> trie.longest_prefix(u'gaz')
KeyError: u'gaz'

>>> trie.longest_prefix(u'gaz', default=u'vasia')
u'vasia'

>>> trie.longest_prefix_item(u'foobarbaz')
(u'foobar', 10)

检查trie是否有具有给定前缀的键

>>> trie.has_keys_with_prefix(u'fo')
True

>>> trie.has_keys_with_prefix(u'FO')
False

从trie中获取具有给定前缀的所有项

>>> trie.keys(u'fo')
[u'foo', u'foobar']

>>> trie.items(u'ba')
[(u'bar', 'bar value')]

>>> trie.values(u'foob')
[10]

从trie中获取以给定前缀开始的特定单词的所有后缀

>>> trie.suffixes()
[u'pro', u'producer', u'producers', u'product', u'production', u'productivity', u'prof']
>>> trie.suffixes(u'prod')
[u'ucer', u'ucers', u'uct', u'uction', u'uctivity']

保存和加载一个trie(值必须是可序列化的)

>>> trie.save('my.trie')
>>> trie2 = datrie.Trie.load('my.trie')

Trie和BaseTrie

在datrie包中有两个Trie类: datrie.Triedatrie.BaseTriedatrie.BaseTrie略快,内存使用也更少,但它只能存储整数,范围是-2147483648 <= x <= 2147483647。 datrie.Trie稍慢,但可以存储任何Python对象作为值。

如果您不需要值或整数值是可以接受的,则使用datrie.BaseTrie

import datrie
import string
trie = datrie.BaseTrie(string.ascii_lowercase)

自定义迭代

如果内置的trie方法不适合您,则可以使用 datrie.Statedatrie.Iterator 来实现自定义遍历。

例如,让我们为我们的trie查找所有以'fo'结尾的后缀并获取值

>>> state = datrie.State(trie)
>>> state.walk(u'foo')
>>> it = datrie.Iterator(state)
>>> while it.next():
...     print(it.key())
...     print(it.data))
o
5
obar
10

性能

性能是在与Python字典比较的情况下测量的,其中字典有100k个唯一的unicode单词(英语和俄语)作为键,'1'作为值。

datrie.Trie大约使用5M内存来存储100k个单词;根据我的非科学的测试,Python字典大约使用22M。

这个trie实现比python的dict在__getitem__上的速度慢2-6倍;基准测试结果(macbook air i5 1.8GHz,“1.000M ops/sec” == “1 000 000 operations per second”)

Python 2.6:
dict __getitem__: 7.107M ops/sec
trie __getitem__: 2.478M ops/sec

Python 2.7:
dict __getitem__: 6.550M ops/sec
trie __getitem__: 2.474M ops/sec

Python 3.2:
dict __getitem__: 8.185M ops/sec
trie __getitem__: 2.684M ops/sec

Python 3.3:
dict __getitem__: 7.050M ops/sec
trie __getitem__: 2.755M ops/sec

查找给定单词的前缀几乎和__getitem__一样快(结果针对Python 3.3)

trie.iter_prefix_items (hits):      0.461M ops/sec
trie.prefix_items (hits):           0.743M ops/sec
trie.prefix_items loop (hits):      0.629M ops/sec
trie.iter_prefixes (hits):          0.759M ops/sec
trie.iter_prefixes (misses):        1.538M ops/sec
trie.iter_prefixes (mixed):         1.359M ops/sec
trie.has_keys_with_prefix (hits):   1.896M ops/sec
trie.has_keys_with_prefix (misses): 2.590M ops/sec
trie.longest_prefix (hits):         1.710M ops/sec
trie.longest_prefix (misses):       1.506M ops/sec
trie.longest_prefix (mixed):        1.520M ops/sec
trie.longest_prefix_item (hits):    1.276M ops/sec
trie.longest_prefix_item (misses):  1.292M ops/sec
trie.longest_prefix_item (mixed):   1.379M ops/sec

查找以给定前缀开始的单词主要受总结果计数限制(这可以在未来得到改善,因为大量的时间被花在将utf_32_le字符串解码为Python的unicode上)

trie.items(prefix="xxx"), avg_len(res)==415:        0.609K ops/sec
trie.keys(prefix="xxx"), avg_len(res)==415:         0.642K ops/sec
trie.values(prefix="xxx"), avg_len(res)==415:       4.974K ops/sec
trie.items(prefix="xxxxx"), avg_len(res)==17:       14.781K ops/sec
trie.keys(prefix="xxxxx"), avg_len(res)==17:        15.766K ops/sec
trie.values(prefix="xxxxx"), avg_len(res)==17:      96.456K ops/sec
trie.items(prefix="xxxxxxxx"), avg_len(res)==3:     75.165K ops/sec
trie.keys(prefix="xxxxxxxx"), avg_len(res)==3:      77.225K ops/sec
trie.values(prefix="xxxxxxxx"), avg_len(res)==3:    320.755K ops/sec
trie.items(prefix="xxxxx..xx"), avg_len(res)==1.4:  173.591K ops/sec
trie.keys(prefix="xxxxx..xx"), avg_len(res)==1.4:   180.678K ops/sec
trie.values(prefix="xxxxx..xx"), avg_len(res)==1.4: 503.392K ops/sec
trie.items(prefix="xxx"), NON_EXISTING:             2023.647K ops/sec
trie.keys(prefix="xxx"), NON_EXISTING:              1976.928K ops/sec
trie.values(prefix="xxx"), NON_EXISTING:            2060.372K ops/sec

与dict相比,随机插入时间非常慢,这是双数组tries的限制;更新相当快。如果您想构建一个trie,请考虑在插入之前对键进行排序

dict __setitem__ (updates):            6.497M ops/sec
trie __setitem__ (updates):            2.633M ops/sec
dict __setitem__ (inserts, random):    5.808M ops/sec
trie __setitem__ (inserts, random):    0.053M ops/sec
dict __setitem__ (inserts, sorted):    5.749M ops/sec
trie __setitem__ (inserts, sorted):    0.624M ops/sec
dict setdefault (updates):             3.455M ops/sec
trie setdefault (updates):             1.910M ops/sec
dict setdefault (inserts):             3.466M ops/sec
trie setdefault (inserts):             0.053M ops/sec

其他结果(注意,len(trie)当前是通过trie遍历实现的)

dict __contains__ (hits):    6.801M ops/sec
trie __contains__ (hits):    2.816M ops/sec
dict __contains__ (misses):  5.470M ops/sec
trie __contains__ (misses):  4.224M ops/sec
dict __len__:                334336.269 ops/sec
trie __len__:                22.900 ops/sec
dict values():               406.507 ops/sec
trie values():               20.864 ops/sec
dict keys():                 189.298 ops/sec
trie keys():                 2.773 ops/sec
dict items():                48.734 ops/sec
trie items():                2.611 ops/sec

请将这些基准测试结果视为仅供参考;这是一个非常简单的基准测试,可能无法覆盖您的用例。

当前限制

  • 键必须是unicode(Python 2.x下,字节字符串没有隐式转换,抱歉);

  • 没有键/值/项的迭代器版本(这尚未实现);

  • 在pypy下,它非常慢,可能还有bug;

  • 库未针对窄Python构建进行测试。

贡献

开发发生在github上:https://github.com/pytries/datrie

请随时提交想法、错误报告和pull请求。

运行测试和基准测试

请确保tox已安装并运行

$ tox

从源代码签出。测试应在Python 2.7和3.4+下通过。

$ tox -c tox-bench.ini

运行基准测试。

如果您已经更改了源代码,请确保已安装cython并运行

$ update_c.sh

每次在执行tox命令之前。

请注意,基准测试数据不包括在发布版本的tar.gz中,因为基准数据很大,这可以节省大量带宽;请使用来自github或bitbucket的源代码签出来进行基准测试。

作者 & 贡献者

请参阅https://github.com/pytries/datrie/graphs/contributors

此模块基于Theppitak Karoonboonyanan的C库libdatrie,并受到Ruby绑定fast_trie、纯Python实现PyTrie和Perl实现Tree::Trie的启发;一些文档和API想法借鉴了这些项目。

许可

根据LGPL v2.1授权。CHANGES =======

0.8.2 (2020-03-25)

  • 通过将cython作为构建时间依赖项并从仓库(和sdist)中删除cython生成的c文件来提供向后兼容的Python支持。

  • 修复collections.abc.MutableMapping导入

  • CI和测试更新

  • 调整库名为不破坏某些链接器

0.8.1(跳过)

本版本有意跳过

0.8 (2019-07-03)

  • Python 3.7兼容性;扩展程序使用Cython 0.29.11重新构建。

  • Trie.get函数;

  • 删除对Python 2.6和3.3的支持;

  • 删除不再需要的libdatrie补丁;

  • 测试和CI修复。

0.7.1 (2016-03-12)

  • 将捆绑的C库更新到版本0.2.9;

  • 使用trie_enumerate实现了Trie.__len__

  • 使用Cython 0.23.4重新构建Cython包装器;

  • Trie更改为实现collections.abc.MutableMapping

  • 修复了在Python2.X上导致段错误的Trie序列化;

0.7 (2014-02-18)

  • 捆绑的libdatrie C库更新到版本0.2.8;

  • 新增了.suffixes()方法(感谢Ahmed T. Youssef);

  • 使用Cython 0.20.1重新构建包装器。

0.6.1 (2013-09-21)

  • 修复了Visual Studio的构建(感谢Gabi Davar)。

0.6 (2013-07-09)

  • 使用Cython 0.19.1重新构建datrie;

  • iter_prefix_valuesprefix_valueslongest_prefix_value方法用于datrie.BaseTriedatrie.Trie(感谢Jared Suttles)。

0.5.1 (2013-01-30)

  • 最近在longest_prefixlongest_prefix_item中引入的内存泄漏已修复。

0.5 (2013-01-29)

  • longest_prefixlongest_prefix_item方法已修复;

  • 使用Cython 0.18重新构建datrie;

  • 修复了README中的误导性基准测试结果;

  • 将State._walk重命名为State.walk_char。

0.4.2 (2012-09-02)

  • 更新到最新的libdatrie;这使得.keys()方法稍微慢一些,但消除了keys长度的限制。

0.4.1 (2012-07-29)

  • 如果可用,则使用cPickle保存/加载datrie.Trie

0.4 (2012-07-27)

  • libdatrie改进和错误修复,包括C迭代器API支持;

  • 使用datrie.Statedatrie.Iterator提供自定义迭代支持。

  • 速度改进:__length__keysvaluesitems方法应该快2倍。

  • 本版本不支持长度超过32768的keys。

0.3 (2012-07-21)

本版本没有添加新功能或速度改进。

  • datrie.new已弃用;使用具有相同参数的datrie.Trie

  • 小型测试和基准测试改进。

0.2 (2012-07-16)

  • datrie.Trie项可以有任何Python对象作为值(0.1.x版本的Trie变为datrie.BaseTrie);

  • longest_prefixlongest_prefix_items已修复;

  • 保存加载 已重写;

  • setdefault 方法。

0.1.1 (2012-07-13)

  • 支持 Windows(上游 libdatrie 的更改已合并);

  • 许可证从 LGPL v3 更改为 LGPL v2.1,以匹配 libdatrie 许可证。

0.1 (2012-07-12)

初始发布。

项目详情


下载文件

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

源代码分发

datrie-0.8.2.tar.gz (63.3 kB 查看哈希值)

上传时间 源代码

构建分发

datrie-0.8.2-pp373-pypy36_pp73-win32.whl (90.2 kB 查看哈希值)

上传时间 PyPy Windows x86

datrie-0.8.2-pp273-pypy_73-win32.whl (91.3 kB 查看哈希值)

上传时间 PyPy Windows x86

datrie-0.8.2-cp38-cp38-win_amd64.whl (134.7 kB 查看哈希值)

上传时间 CPython 3.8 Windows x86-64

datrie-0.8.2-cp38-cp38-win32.whl (110.1 kB 查看哈希值)

上传时间 CPython 3.8 Windows x86

datrie-0.8.2-cp38-cp38-manylinux1_x86_64.whl (551.6 kB 查看哈希值)

上传时间 CPython 3.8

datrie-0.8.2-cp38-cp38-macosx_10_9_x86_64.whl (157.8 kB 查看哈希值)

上传时间 CPython 3.8 macOS 10.9+ x86-64

datrie-0.8.2-cp37-cp37m-win_amd64.whl (130.8 kB 查看哈希值)

上传时间 CPython 3.7m Windows x86-64

datrie-0.8.2-cp37-cp37m-win32.whl (107.6 kB 查看哈希值)

上传于 CPython 3.7m Windows x86

datrie-0.8.2-cp37-cp37m-manylinux1_x86_64.whl (537.4 kB 查看哈希值)

上传于 CPython 3.7m

datrie-0.8.2-cp37-cp37m-macosx_10_9_x86_64.whl (154.6 kB 查看哈希值)

上传于 CPython 3.7m macOS 10.9+ x86-64

datrie-0.8.2-cp36-cp36m-win_amd64.whl (123.0 kB 查看哈希值)

上传于 CPython 3.6m Windows x86-64

datrie-0.8.2-cp36-cp36m-win32.whl (102.0 kB 查看哈希值)

上传于 CPython 3.6m Windows x86

datrie-0.8.2-cp36-cp36m-manylinux1_x86_64.whl (544.9 kB 查看哈希值)

上传于 CPython 3.6m

datrie-0.8.2-cp36-cp36m-macosx_10_9_x86_64.whl (162.8 kB 查看哈希值)

上传于 CPython 3.6m macOS 10.9+ x86-64

datrie-0.8.2-cp35-cp35m-win_amd64.whl (118.8 kB 查看哈希值)

上传于 CPython 3.5m Windows x86-64

datrie-0.8.2-cp35-cp35m-win32.whl (97.6 kB 查看哈希值)

上传于 CPython 3.5m Windows x86

datrie-0.8.2-cp35-cp35m-manylinux1_x86_64.whl (530.4 kB 查看哈希值)

上传于 CPython 3.5m

datrie-0.8.2-cp35-cp35m-macosx_10_6_x86_64.whl (157.9 kB 查看哈希值)

上传于 CPython 3.5m macOS 10.6+ x86-64

datrie-0.8.2-cp34-cp34m-manylinux1_x86_64.whl (541.2 kB 查看哈希值)

上传时间 CPython 3.4m

datrie-0.8.2-cp27-cp27mu-manylinux1_x86_64.whl (470.1 kB 查看哈希值)

上传时间 CPython 2.7mu

datrie-0.8.2-cp27-cp27m-win_amd64.whl (118.0 kB 查看哈希值)

上传时间 CPython 2.7m Windows x86-64

datrie-0.8.2-cp27-cp27m-win32.whl (98.5 kB 查看哈希值)

上传时间 CPython 2.7m Windows x86

datrie-0.8.2-cp27-cp27m-manylinux1_x86_64.whl (469.6 kB 查看哈希值)

上传时间 CPython 2.7m

datrie-0.8.2-cp27-cp27m-macosx_10_7_x86_64.whl (156.6 kB 查看哈希值)

上传时间 CPython 2.7m macOS 10.7+ x86-64

由以下支持