a_standard = {
'section1': {
'category1': 1,
'category2': 2
},
'section2': {
'category1': 1,
'category2': 2
}
}
a_new = {
'section1': {
'category1': 1,
'category2': 2
},
'section2': {
'category1': 1,
'category2': 3
}
}
我想找到a_standard
和a_new
之间的差异,a_new[section2][category2]
价值差异为2
和3
我应该将每个转换成一个集合,然后做差异或循环并比较字典吗?
答案 0 :(得分:2)
有一个名为deepdiff的库,它具有很多选项,但我发现它有点不直观。
这是我在单元测试期间经常用来计算差异的递归函数。这超出了问题所要解决的范围,因为我也关心列表被嵌套的情况。希望您会发现它有用。
功能定义:
from copy import deepcopy
def deep_diff(x, y, parent_key=None, exclude_keys=[], epsilon_keys=[]):
"""
Take the deep diff of JSON-like dictionaries
No warranties when keys, or values are None
"""
EPSILON = 0.5
rho = 1 - EPSILON
if x == y:
return None
if parent_key in epsilon_keys:
xfl, yfl = float_or_None(x), float_or_None(y)
if xfl and yfl and xfl * yfl >= 0 and rho * xfl <= yfl and rho * yfl <= xfl:
return None
if type(x) != type(y) or type(x) not in [list, dict]:
return x, y
if type(x) == dict:
d = {}
for k in x.keys() ^ y.keys():
if k in exclude_keys:
continue
if k in x:
d[k] = (deepcopy(x[k]), None)
else:
d[k] = (None, deepcopy(y[k]))
for k in x.keys() & y.keys():
if k in exclude_keys:
continue
next_d = deep_diff(x[k], y[k], parent_key=k, exclude_keys=exclude_keys, epsilon_keys=epsilon_keys)
if next_d is None:
continue
d[k] = next_d
return d if d else None
# assume a list:
d = [None] * max(len(x), len(y))
flipped = False
if len(x) > len(y):
flipped = True
x, y = y, x
for i, x_val in enumerate(x):
d[i] = deep_diff(y[i], x_val, parent_key=i, exclude_keys=exclude_keys, epsilon_keys=epsilon_keys) if flipped else deep_diff(x_val, y[i], parent_key=i, exclude_keys=exclude_keys, epsilon_keys=epsilon_keys)
for i in range(len(x), len(y)):
d[i] = (y[i], None) if flipped else (None, y[i])
return None if all(map(lambda x: x is None, d)) else d
# We need this helper function as well:
def float_or_None(x):
try:
return float(x)
except ValueError:
return None
用法:
>>> deep_diff(a_standard, a_new)
{'section2': {'category2': (2, 3)}}
我认为输出比其他答案更直观。
在单元测试中,我会这样:
import json
diff = deep_diff(expected_out, out, exclude_keys=["flickery1", "flickery2"])
assert diff is None, json.dumps(diff, indent=2)
答案 1 :(得分:1)
您可以使用递归:
a_standard = {
'section1': {
'category1': 1,
'category2': 2
},
'section2': {
'category1': 1,
'category2': 2
}
}
a_new = {
'section1': {
'category1': 1,
'category2': 2
},
'section2': {
'category1': 1,
'category2': 3
}
}
def differences(a, b, section=None):
return [(c, d, g, section) if all(not isinstance(i, dict) for i in [d, g]) and d != g else None if all(not isinstance(i, dict) for i in [d, g]) and d == g else differences(d, g, c) for [c, d], [h, g] in zip(a.items(), b.items())]
n = filter(None, [i for b in differences(a_standard, a_new) for i in b])
输出:
[('category2', 2, 3, 'section2')]
产生与不等值相对应的密钥。
编辑:没有列表理解:
def differences(a, b, section = None):
for [c, d], [h, g] in zip(a.items(), b.items()):
if not isinstance(d, dict) and not isinstance(g, dict):
if d != g:
yield (c, d, g, section)
else:
for i in differences(d, g, c):
for b in i:
yield b
print(list(differences(a_standard, a_new)))
输出:
['category2', 2, 3, 'section2']
此解决方案利用生成器(因此yield
语句),它可以动态存储生成的值,只记住它停止的位置。可以通过将返回的结果作为列表转换来获取值。 yield
可以更容易地累积值差异,并且无需在函数或全局变量中保留其他参数。
答案 2 :(得分:1)
假设密钥相同,你可以这样做:
class Player(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pg.Surface((tilesize, tilesize))
self.image.fill(yellow)
self.rect = self.image.get_rect()
self.vx, self.vy = 0, 0
self.x = x * tilesize
self.y = y * tilesize
def get_keys(self):
self.vx, self.vy = 0, 0
keys = pg.key.get_pressed()
if keys[pg.K_LEFT] or keys[pg.K_a]:
self.vx = -playerspeed
if keys[pg.K_RIGHT] or keys[pg.K_d]:
self.vx = playerspeed
if keys[pg.K_UP] or keys[pg.K_w]:
self.vy = -playerspeed
if keys[pg.K_DOWN] or keys[pg.K_s]:
self.vy = playerspeed
if self.vx != 0 and self.vy != 0:
self.vx *= 0.7071
self.vy *= 0.7071
def collide_with_walls(self, dir):
if dir == 'x':
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vx > 0:
self.x = hits[0].rect.left - self.rect.width
if self.vx < 0:
self.x = hits[0].rect.right
self.vx = 0
self.rect.x = self.x
if dir == 'y':
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vy > 0:
self.y = hits[0].rect.top - self.rect.height
if self.vy < 0:
self.y = hits[0].rect.bottom
self.vy = 0
self.rect.y = self.y
def update(self):
self.get_keys()
self.x += self.vx * self.game.dt
self.y += self.vy * self.game.dt
self.rect.x = self.x
self.collide_with_walls('x')
self.rect.y = self.y
self.collide_with_walls('y')
我现在正在手机上打字,很抱歉,如果语法有些混乱。