正则表达式可以用作字典中的键吗?
Posted
技术标签:
【中文标题】正则表达式可以用作字典中的键吗?【英文标题】:Can a regular expression be used as a key in a dictionary? 【发布时间】:2016-01-25 10:19:36 【问题描述】:我想创建一个字典,其中键是正则表达式:
d = 'a.*': some_value1, 'b.*': some_value2
然后,当我查字典时:
d['apple']
我希望苹果'apple'
与正则表达式的键相匹配。如果与键/正则表达式完全匹配,则应返回相应的值。
例如'apple'
与正则表达式'a.*'
完全匹配,所以应该返回some_value1
。
当然,所有这些都假设正则表达式键不冲突(即两个键不应该都完全匹配同一个字符串)。假设我可以在构建密钥时手动处理此要求。
这在 Python 中可行吗?如果是这样,那将是一个相当优雅和强大的构造!
【问题讨论】:
“如果是这样,那将是一个非常优雅/强大的构造!” - 不,它不会,因为你可以编写无限数量的正则表达式匹配相同的字符串。你没有问正确的问题。请说明您要解决的问题。 你想要两个不兼容的东西。一个程序不能确定一个正则表达式是否匹配一个字符串,至少需要同时查看正则表达式和字符串。给定一个 set 正则表达式(键)和一个字符串,知道哪个正则表达式匹配该字符串的唯一方法是尝试每个正则表达式。但是字典的性能完全取决于它找到匹配键的能力 不必遍历所有键。因此,即使您可以避免键中的所有歧义,其性能仍然不会比键值对列表好。 Re Tomalak 的评论:是的,请解释您要解决的问题。从表面上看,为了利用dict
的快速哈希表查找机制,您最好的选择是将键设置为字符串的第一个字符,即“a”、“b”。请您解释一下您希望从另一种安排中获得什么。
【参考方案1】:
你可以使用它。这个对我有用。您需要确保您的密钥是唯一的。
word_mapping =
r'\W*J\W*2\W*W\W*' : ' j2w ', r'\W*power\W*on\W*': ' poweron ',
r'\W*Some From Dase [0-9]*\W*' : ''
def correct_word_mapping(x, dic):
for word in dic.keys():
#if word in x:
x = re.sub(word, dic[word], x, flags=re.IGNORECASE)
return x
print(correct_word_mapping("power on J 2 w PowerONJ2 w", word_mapping))
【讨论】:
【参考方案2】:可能的解决方案:
import re
class RegexDict(dict):
def __init__(self):
super(RegexDict, self).__init__()
def __getitem__(self, item):
for k, v in self.iteritems():
if re.match(k, item):
return v
raise KeyError
if __name__ == '__main__':
d = RegexDict()
d[r'a.*'] = 'Informed a key that starts with a'
d[r'b.*'] = 'Informed a key that starts with b'
d[r'\w+'] = 'Informed alphanumeric as key'
d[r'\d+'] = 'Informed numbers as key'
d[r'\s+'] = 'Informed spaces as key'
print d[' ']
print d['apple']
print d['bee']
print d['123']
print d['word']
print d['word1']
输出:
Informed spaces as key
Informed a key that starts with a
Informed a key that starts with b
Informed numbers as key
Informed alphanumeric as key
Informed alphanumeric as key
【讨论】:
【参考方案3】:Python 字典被实现为 hash tables - 这意味着通过内部散列 myvalue
,任何 mydict[myvalue]
查找都非常快。使用正则表达式作为键将取消此功能。而不是使用字典,您应该使用一个简单的列表或元组,其中每个项目都是格式为:(pattern/compiled regular expression, value)
的元组并扫描它们直到正则表达式通过。这也将使您能够使用正则表达式的顺序(例如,从特定到一般):
import re
LOOKUPS = [
('a.*', 'a'),
('b.*', 'b'),
]
def lookup(s, lookups):
for pattern, value in lookups:
if re.search(pattern, s):
return value
return None
print(lookup("apple", LOOKUPS))
另请参阅 Django 的 url resolver,了解您的想法的(非常)高级实现。
【讨论】:
很好,谢谢!我可以建议在找到匹配项后在循环内使用break
,而不是在函数中使用两个return
语句。然后在最后返回存储的值。【参考方案4】:
您可以使用re.compile
d 模式对象作为字典键:
>>> import re
>>> regex = re.compile('a.*')
>>> d = regex: 'foo'
>>> d[re.compile('a.*')]
'foo'
请注意,重新编译同一个正则表达式会给你一个相等的键(同一个对象,实际上是:re.compile('a.*') is d.keys()[0]
),所以你可以取回你存储的任何东西。
但是:
正如cmets中指出的,多个正则表达式可以匹配同一个字符串; 字典没有排序,因此每次运行程序时可能会首先得到不同的匹配正则表达式;和 没有O(1)
方法可以向字典regex: result, ...
查询result
值,因为字符串可能匹配一个或多个regex
键。
因此很难看出您会为此找到什么实用程序。
如果您可以想出一种方法来确保没有两个键可以匹配相同的字符串,您可以创建一个 MutableMapping
子类,当您添加新键并实现 @ 时应用此检查987654330@ 扫描键值对并返回参数与键正则表达式匹配的第一个值。不过,这又是O(n)
。
【讨论】:
【参考方案5】:当然。只需正常查找它们并检查匹配项。
import re
def find_matches(d, item):
for k in d:
if re.match(k, item):
return d[k]
d = 'a.*': 'a match', 'b.*': 'b match'
for item in ['apple', 'beer']:
print(find_matches(d, item))
结果:
a match
b match
请注意,re.match
只有在字符串的开头处找到表达式时才会产生匹配。如果表达式可以位于字符串中的任何位置,请使用 re.search
。
【讨论】:
以上是关于正则表达式可以用作字典中的键吗?的主要内容,如果未能解决你的问题,请参考以下文章