我希望能够在混合类型字典中进行比较(包含int,float,strings,numpy.arrays)。我的最小示例有一个字典列表,我想要一个函数(或生成器)迭代该列表并挑选包含键值对的元素(dicts),由** kwargs输入到该函数(或生成器)指定)。
import re
list_of_dicts = [{'s1':'abcd', 's2':'ABC', 'i':42, 'f':4.2},
{'s2':'xyz', 'i':84, 'f':8.4}]
def find_list_element(**kwargs):
for s in list_of_dicts:
for criterion, criterion_val in kwargs.iteritems():
if type(criterion_val) is str:
if re.match(criterion_val, s.get(criterion, 'unlikely_return_val')):
yield s
continue
if s.get(criterion, None) == criterion_val:
yield s
print [a for a in find_list_element(i=41)] # []
print [a for a in find_list_element(i=42)] # [{'i': 42, 's2': 'ABC', 's1': 'abcd', 'f': 4.2}]
print [a for a in find_list_element(s1='xyz')] # []
print [a for a in find_list_element(s2='xyz')] # [{'i': 84, 's2': 'xyz', 'f': 8.4}]
print [a for a in find_list_element(s2='[a-z]')] # [{'i': 84, 's2': 'xyz', 'f': 8.4}]
我上面的两个问题是:
如果函数要求比较是一个字符串,我想切换到正则表达式匹配(re.search或re.match)而不是纯字符串比较。在上面的代码中,这是通过辱骂类型检查完成的,它看起来并不那么优雅。是否有更好的解决方案不涉及类型检查?或者,这可能是在python中允许类型检查的情况?
**kwargs
当然可以包含多个比较。目前我只能想到一个涉及一些标志的解决方案(found = False
切换到found = True
并在list_of_dicts
的每次迭代结束时进行评估。在决定是否屈服之前,是否有一些巧妙的方法来积累每个s的比较结果?
有没有什么方法可以让这一整套文章变得更漂亮?
PS:此实际使用案例涉及获取的MRI数据集(BRUKER)的表示。数据集通过我已转换为dicts的参数文件来表征,这些dicts是表示所述扫描的对象的一部分。我正在收集这些数据集,并希望根据这些参数文件给出的某些条件进一步过滤它们。这些参数可以是字符串,数字和其他一些不太方便的类型。
如果我想从@BrenBarn和@srgerg的输入中得出一致意见答案,那就是
list_of_dicts = [{'s1':'abcd', 's2':'ABC', 'i':42, 'f':4.2},
{'s2':'xyz', 'i':84, 'f':8.4}]
# just making up some comparison strategies
def regex_comp(a,b): return re.match(a,b)
def int_comp(a,b): return a==b
def float_comp(a,b): return round(a,-1) == round (b,-1)
pre_specified_comp_dict = {frozenset(['s1','s2']) : regex_comp,
frozenset(['i']): int_comp,
frozenset(['f']): float_comp}
def fle_new(**kwargs):
chosen_comps={}
for key in kwargs.keys():
# remember, the keys here are frozensets
cand_comp = [x for x in pre_specified_comp_dict if key in x]
chosen_comps[key] = pre_specified_comp_dict[cand_comp[0]]
matches = lambda d: all(k in d and chosen_comps[k](v, d[k])
for k, v in kwargs.items())
return filter(matches, list_of_dicts)
现在唯一的挑战是提出一个创建pre_specified_comp_dict
的无痛策略。
答案 0 :(得分:2)
在这种情况下,我似乎可以使用类型检查,因为根据类型,你确实需要完全不同的行为。但是,你应该让你的类型检查变得更聪明。使用if isinstance(criterion_val, basestring)
而不是直接检查str
类型。这样,它仍然适用于unicode字符串。
避免类型检查的方法是预先指定每个字段的比较类型。查看您的示例数据,看起来每个字段始终具有一致的类型(例如,s1始终是字符串)。如果是这种情况,您可以在字段名称和比较类型之间创建显式映射,例如:
regex_fields = ['s1', 's2']
然后在代码中,而不是类型检查,执行if criterion in regex_fields
以查看该字段是否应与正则表达式进行比较。如果您只有两种类型的比较,则可以将dict映射字段名称用于比较操作的某种ID。
这样做的好处是它可以更明确地对您的假设进行编码,这样如果有一些奇怪的数据进入(例如,您期望数字的字符串),将会引发错误,而不是默默地应用类型相应的比较。它还使字段和比较之间的关系“保持独立”,而不是将其隐藏在实际比较代码的中间。
如果你有大量的字段对它们的不同子集进行了许多不同的比较操作,那么这可能尤其值得。在这种情况下,最好预先定义哪些比较适用于哪个字段名称(而不是哪种类型),而不是为每次比较即时决定。只要你总是根据字段名称知道要做什么类型的比较,这将使事情变得更加清洁。如果你需要添加一个新字段,它会增加维护开销,所以如果这只是私人观众的脚本,我可能不会这样做。
答案 1 :(得分:1)
以下是我实施find_list_element
功能的方法。它仍然使用The Reviled Type Checking(TM),但它看起来更有说服力恕我直言:
def find_list_element(**kwargs):
compare = lambda e, a: re.match(e, a) is not None if isinstance(e, str) else e == a
matches = lambda d: all(k in d and compare(v, d[k]) for k, v in kwargs.items())
return filter(matches, list_of_dicts)
(顺便说一下,我使用的是Python 3,虽然代码在Python 2.7中有效但是应该使用basestring
而不是str
,正如BrenBarn已经指出的那样。)
请注意,我使用了Python的all函数来避免累积比较结果。
答案 2 :(得分:0)
您可以在下面看到我的代码,它解决了多个比较的需要:
def find_dict(**kwargs):
for data in lds: # lds is the same as list_of_dicts
for key, val in kwargs.iteritems():
if not data.get(key, False) == val: return False
else:
yield data
O / P:
find_dict(i=42, s1='abcd')
{'i': 42, 's2': 'ABC', 's1': 'abcd', 'f': 4.2}
我没有包含正则表达式比较的代码!
干杯!