[英]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.