问题描述
如何检查一个字典是否与另一个字典匹配?例如,给定 2 个字典
{
name: "Alice",lives: {
city: "Venice",since: 2000
}
}
{
name: "Bob",lives: {
city: "Hong Kong"
}
}
匹配
{
name: "^A.*",<- a regexp
lives: {
since: "*" <- must have this key too
}
}
该函数将返回 True 或 False。
解决方法
一种解决方案是使用 validation
方法创建自定义类,并在构建匹配对象时为字段使用特定值:
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)
打印:
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
您还可以轻松添加对城市名称等的检查。
干杯!
,如果您想对规范进行硬编码,解决方案相当简单。
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))
现在让我们假设您想要更加动态并拥有一个接受任何此类规范的函数。
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': '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
也许这会有所帮助