使用正则表达式在Python字典中搜索键对应的值

53

我希望实现在Python字典中通过正则表达式作为键值来搜索特定键的值。

例如:

我有一个Python字典,其中包含如下值:

{'account_0':123445,'seller_account':454545,'seller_account_0':454676, 'seller_account_number':3433343}

我需要搜索其键包含“seller_account”的值?我写了一个示例程序,但想知道是否有更好的解决方案。主要原因是我不确定正则表达式,并可能会忽略某些内容(例如,如何设置以“seller_account”开头的键的re):

#!usr/bin/python
import re
my_dict={'account_0':123445,'seller_account':454545,'seller_account_0':454676, 'seller_account_number':3433343}

reObj = re.compile('seller_account')

for key in my_dict.keys():
        if(reObj.match(key)):
                print key, my_dict[key]

~ home> python regular.py

seller_account_number 3433343
seller_account_0 454676
seller_account 454545

如果您经常需要搜索这些关键部分,那么您的数据结构可能存在问题。 - eumiro
11
每当我看到像eumiro这样的评论时,我就会觉得有人想象出一个完美、整体的世界,在那里OP可以完全控制她工作的所有方面。你真的认为@Programmer控制着正在接收的数据结构吗?除非这是一个学校项目,否则我非常怀疑。 - Mike Williamson
5个回答

61
如果你只需要检查以 "seller_account" 开头的键,那么你不需要正则表达式,只需使用 startswith() 函数。
my_dict={'account_0':123445,'seller_account':454545,'seller_account_0':454676, 'seller_account_number':3433343}

for key, value in my_dict.iteritems():   # iter on both keys and values
        if key.startswith('seller_account'):
                print key, value

或者用一行代码表达:

result = [(key, value) for key, value in my_dict.iteritems() if key.startswith("seller_account")]

NB:对于Python 3.X版本,请将 iteritems() 替换为 items(),并不要忘记为 print 添加 ()


3
要再次将 result 变成字典,你可以使用 dict() 或者(在Python 2.7中)使用花括号 {...},而不是列表推导式 [...] - Dr. Jan-Philip Gehrcke
7
在Python3中,您需要使用items()而不是iteritems() - Arash
1
但是这样你就失去了字典的效率,不是吗?访问不再是O(1),因为你必须在字典元素上进行for循环。 - user5054
1
你无论如何都在手动搜索每个键,因此效率对我们来说并没有太大作用。 - Shayne

11

您可以使用 dpath 解决此问题。

http://github.com/akesterson/dpath-python

dpath 允许您使用键的 glob 语法搜索字典,并过滤值。您想要的是微不足道的:

$ easy_install dpath
>>> dpath.util.search(MY_DICT, 'seller_account*')

那会给你返回一个大的合并字典,其中包含所有与该 glob 匹配的键。如果你只需要路径和值:

$ easy_install dpath
>>> for (path, value) in dpath.util.search(MY_DICT, 'seller_account*', yielded=True):
>>> ... # do something with the path and value

5
安德鲁,我建议你小心发布没有披露的内容。你所有的答案都与“dpath”有关。显然你很喜欢(或是其中一员)dpath;如果是这样,我想提醒你必须披露你的隶属关系。 - Jesse
19
从 Github 的链接来看,Andrew 似乎是 dpath 的主要作者。鉴于这是 MIT 许可的软件,我认为没有任何问题。 - dalloliogm

8
def search(dictionary, substr):
    result = []
    for key in dictionary:
        if substr in key:
            result.append((key, dictionary[key]))   
    return result

>>> my_dict={'account_0':123445,'seller_account':454545,'seller_account_0':454676, 'seller_account_number':3433343}
>>> search(my_dict, 'seller_account')
[('seller_account_number', 3433343), ('seller_account_0', 454676), ('seller_account', 454545)]

1
你可以通过将搜索词和键转换为小写字母来使搜索更加通用,例如:if substr.lower() in key.lower()。这样可以忽略大小写的差异。 - psychemedia

7

您可以使用“re”和“filter”的组合。例如,如果您想要搜索在os模块中方法名称中包含单词“stat”的方法,可以使用以下代码。

import re 
import os
r = re.compile(".*stat.*")
list(filter(r.match, os.__dict__.keys()))

结果是:

['stat', 'lstat', 'fstat', 'fstatvfs', 'statvfs', 'stat_result', 'statvfs_result']

我认为原问题中的性能问题是在使用"re"模块找到键之后进行关键值搜索。如果关键部分是可交换的,则无法使用"startswith"。因此,"re"是一个不错的选择。同时,我使用了过滤器来获取所有匹配键的列表,并将它们制作成列表,以便我们可以通过简单的[DICT[k] for k in LIST]返回所有值。


我可以问一下你的回答与原问题——关于搜索字典有什么关系吗? - Thomas Fritz
1
我认为原问题中的性能问题在于使用“re”模块找到键后进行关键值搜索。如果关键字的一部分是可互换的,则无法使用“startswith”,因此“re”是一个很好的选择。 另外,我使用过滤器来获取所有匹配的键的列表,并将它们组成一个列表,以便我们可以通过简单的 [DICT[k] for k in LIST] 返回所有值。 - Ehsan Ahmadi
@EhsanAhmadi 你应该在答案中添加你的评论,使其更加精确。 - Clintm
这是一个不错的解决方案。谢谢你提供它。我忘记了过滤器。 - Petra Kahn

0
“如何设置以'seller_account'开头的键的re?”
reObj = re.compile('seller_account')

应该是:

reObj = re.compile('seller_account.*')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接