将混合的dict元素与int,字符串和浮点数进行比较

时间:2013-04-08 05:22:31

标签: python dictionary generator string-comparison

我希望能够在混合类型字典中进行比较(包含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}]

我上面的两个问题是:

  1. 如果函数要求比较是一个字符串,我想切换到正则表达式匹配(re.search或re.match)而不是纯字符串比较。在上面的代码中,这是通过辱骂类型检查完成的,它看起来并不那么优雅。是否有更好的解决方案不涉及类型检查?或者,这可能是在python中允许类型检查的情况?

  2. **kwargs当然可以包含多个比较。目前我只能想到一个涉及一些标志的解决方案(found = False切换到found = True并在list_of_dicts的每次迭代结束时进行评估。在决定是否屈服之前,是否有一些巧妙的方法来积累每个s的比较结果?

  3. 有没有什么方法可以让这一整套文章变得更漂亮?

    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的无痛策略。

3 个答案:

答案 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}

我没有包含正则表达式比较的代码!

干杯!