简体   繁体   English

如何检查字典是否与 Python 中的另一个字典匹配?

[英]How to check if a dictionary matches another dictionary in Python?

How do I check if a dictionary matches another dictionary?如何检查一个字典是否与另一个字典匹配? For example, given 2 dictionaries例如,给定 2 个字典

{
    name: "Alice",
    lives: {
        city: "Venice",
        since: 2000
    }
}

{
    name: "Bob",
    lives: {
        city: "Hong Kong"
    }
}

match with匹配

{
    name: "^A.*", <- a regexp
    lives: {
        since: "*" <- must have this key too
    }
}

and the function will return True or False. function 将返回 True 或 False。

A solution would be to create a custom class with a validation method, and use specific values for the field when building the match object:一种解决方案是使用validation方法创建自定义 class,并在构建匹配 object 时为该字段使用特定值:

from dataclasses import dataclass
import re
from typing import Optional

@dataclass
class City:
    name: str
    since: Optional[int] = None

    def __str__(self):
        if self.since is not None:
            return f"{self.name} since {self.since}"
        else:
            return f"{self.name}"

@dataclass
class Person:
    name: str
    lives: City

    def __str__(self):
        return f"{self.name} lives in {self.lives}"

    def validate(self, match_spec):
        valid = True

        # validate the name
        m = re.match(match_spec.name, self.name)
        if m is not None:
            print(f"Name {self.name} does not match {match_spec.name}")
            valid = False

        # check if the spec requires the year
        if match_spec.lives.since is not None:
            if self.lives.since is None:
                print(f"Missing year for {self.name}.")
                valid = False

        if valid:
            print("Correct validation")

alice = Person("Alice", City("Venice", 2000))
print(alice)
bob = Person("Bob", City("Hong Kong"))
print(bob)

# we change the semantic of person.lives.since:
# a not None value means that we want to check that the field exists
match_spec_1 = Person("^A.*", City(".*", 1))

print(f"\nValidate '{alice}' with '{match_spec_1}''")
alice.validate(match_spec_1)

print(f"\nValidate '{bob}' with '{match_spec_1}'")
bob.validate(match_spec_1)

match_spec_2 = Person("^B.*", City(".*"))

print(f"\nValidate '{alice}' with '{match_spec_2}'")
alice.validate(match_spec_2)

print(f"\nValidate '{bob}' with '{match_spec_2}'")
bob.validate(match_spec_2)

This prints:这打印:

Alice lives in Venice since 2000
Bob lives in Hong Kong

Validate 'Alice lives in Venice since 2000' with '^A.* lives in .* since 1''
Correct validation

Validate 'Bob lives in Hong Kong' with '^A.* lives in .* since 1'
Name Bob does not match ^A.*
Missing year for Bob.

Validate 'Alice lives in Venice since 2000' with '^B.* lives in .*'
Name Alice does not match ^B.*

Validate 'Bob lives in Hong Kong' with '^B.* lives in .*'
Correct validation

And you could easily add checks on the city name and so on.您可以轻松地添加对城市名称等的检查。

Cheers!干杯!

If you want to hard code the spec, the solution is fairly easy.如果您想对规范进行硬编码,解决方案相当简单。

import re

def meets_spec(dic):
    # Here we just assume the pattern is constant
    pattern = re.compile(r'^A.*')
    # if the pattern has matches and the key 'since'
    # is in the 'lives' dict, return True
    if re.match(pattern, dic.get('name', '')):
        # .get() is nice here because it doesn't raise an
        # error when the key doesn't exist. It just
        # returns False and moves on
        if 'since' in dic.get('lives', {}):
            return True
    return False

dict_1 = {
    'name': 'Alice',
    'lives': {
        'city': 'Venice',
        'since': 2000
    }
}
dict_2 = {
    'name': 'Bob',
    'lives': {
        'city': 'Hong Kong'
    }
}
print(meets_spec(dict_1))
print(meets_spec(dict_2))

Now let's assume you want to be a bit more dynamic and have a function that accepts any such spec.现在让我们假设您想要更加动态并且拥有一个接受任何此类规范的 function。

import re

def meets_spec(dic, spec, required='*'):
    # by default we return True
    # and conditionally prove ourselves False
    result = True
    for k, v in spec.items():
        # this assumes an asterisk is a required item
        # but still allows what defines required to be changed
        if v == required:
            if k not in dic:
                result = False
        # it checks nested dicts recursively
        elif isinstance(v, dict):
            # .get() is used for the same reasons above.
            if not meets_spec(dic.get(k, {}), v):
                result = False
        # and it assumes anything that isn't an asterisk
        # or a nested dict is a regular expression.
        else:
            pattern = re.compile(v)
            if not re.match(pattern, dic.get(k, '')):
                result = False
    return result

dict_1 = {
    'name': 'Alice',
    'lives': {
        'city': 'Venice',
        'since': 2000
    }
}
dict_2 = {
    'name': 'Bob',
    'lives': {
        'city': 'Hong Kong'
    }
}
spec = {
    'name': '^A.*',
    'lives': {
        'since': '*'
    }
}
print(meets_spec(dict_1, spec))
print(meets_spec(dict_2, spec))
import re

a = {
    'name': "Alice",
    'lives': {
        'city': "Venice",
        'since': 2000
    }
}

b = {
    'name': "Bob",
    'lives': {
        'city': "Hong Kong"
    }
}

match_with = {
    'name': "^A.*",
    'lives': {
        'since': "*"
    }
}

matched = {
    'name': False,
    'since': False
}


def match_dict(dictionary, match_with):
    for k, v in dictionary.items():
        if k in match_with:
            if type(v) == dict:
                match_dict(v, match_with[k])
            if k == 'name':
                if re.match(match_with[k], dictionary[k]):
                    matched['name'] = True
                    continue
            elif k == 'since':
                matched['since'] = True

    if False in matched.values():
        return False
    else:
        return True

maybe this will help也许这会有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM