以简单易读的方式过滤字典

时间:2017-10-03 12:48:01

标签: python python-3.x dictionary data-structures

使用python3.4我试图过滤导出为JSON并转换为字典的防火墙规则库。我想根据用户定义的标准进行过滤,但是如果没有难以阅读的构造有很多循环和if语句,我似乎无法做到这一点。

我的防火墙条目中的字典很大,所以我把它们缩短了一点。

2个例子:

entry1 = {'action': 'accept',
          'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}],
          'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
         }
entry2 = {'action': 'accept',
          'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}],
          'srcintf': [{'name': 'ZN_DMZ', 'q_origin_key': 'ZN_DMZ'},
                      {'name': 'ZN_MGMT', 'q_origin_key': 'ZN_MGMT'},
                      {'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
         }

我想创建一个数据结构来与2个示例进行比较,并创建如下:

filter = {'action': 'accept',
          'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
         }

在搜索了如何比较这些结构后,我最终得到了一些可读的代码。我的问题是它在entry2(它有多个源接口)时没有评估为true:

>>> filter.items() <= entry1.items()
True
>>> filter.items() <= entry2.items()
False

关于我应该如何做的任何提示?

编辑: 使用下面的答案Eric Duminil我可以创建一些东西(见下文),虽然它仍然没有我想要的那么可读。任何进一步的提示?

example = entry2
# Compare entry to filter
noMatch = 0
for key in filter:
    if isinstance(example[key], list):
        # Convert list of dicts to list for easier comparing
        tmpExample = [d['name'] for d in example[key]]
        # Break if entry does not contain all criteria
        if not all(value in tmpExample for value in filter[key]):
            noMatch = 1
            print("No match on: " + str(filter[key]))
            break
    elif filter[key] != example[key]:
        # Simple string comparing
        noMatch = 1
        print("No match on: " + str(filter[key]))
        break

if noMatch == 0:
    print("Match")
else:
    print(" No match")

1 个答案:

答案 0 :(得分:4)

理论

'dstintf''srcintf'中的数据结构不会使此问题更容易。 dicts列表几乎不是正确的类型。

根据您的需要,您应该将其转换为字典:

>>> data = [{'name': 'ZN_DMZ', 'q_origin_key': 'ZN_DMZ'},
...                       {'name': 'ZN_MGMT', 'q_origin_key': 'ZN_MGMT'},
...                       {'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
>>> {d['q_origin_key']:d['name'] for d in data}
{'ZN_AUDIT': 'ZN_AUDIT', 'ZN_MGMT': 'ZN_MGMT', 'ZN_DMZ': 'ZN_DMZ'}

但如果nameq_origin_key的值始终相等,则可以使用列表或集合:

>>> [d['name'] for d in data]
['ZN_DMZ', 'ZN_MGMT', 'ZN_AUDIT']
>>> {d['name'] for d in data}
set(['ZN_AUDIT', 'ZN_MGMT', 'ZN_DMZ'])

现在过滤您的数据应该更容易,没有循环或if s。

最后,dict.items()以任意顺序返回元组列表。

我认为dict1.items() <= dict2.items()无助于您做任何事情。

实施例

将代码拆分为多个函数并使用集合可能是个好主意:

query = {'action': 'accept',
         'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
         }

entry1 = {'action': 'accept',
          'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}],
          'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
          }

entry2 = {'action': 'accept',
          'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}],
          'srcintf': [{'name': 'ZN_DMZ', 'q_origin_key': 'ZN_DMZ'},
                      {'name': 'ZN_MGMT', 'q_origin_key': 'ZN_MGMT'},
                      {'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}]
          }


def compare_values(value1, value2):
    if isinstance(value1, list) and isinstance(value2, list):
        return set(d['name'] for d in value1).issubset(set(d['name'] for d in value2))
    else:
        return value1 == value2

def is_a_match(query, entry):
    do_not_match = [key for key in query if not compare_values(
        query[key], entry.get(key))]
    for key in do_not_match:
        print("%r does not match" % key)
    return len(do_not_match) == 0

print(is_a_match(query, entry1))
# True
print(is_a_match(query, entry2))
# True
print(is_a_match({'action': 'decline'}, entry2))
# 'action' does not match
# False