L'approche de Scott en utilisant collections.Counter
est agréable, mais elle a l'inconvénient de ne pas être utilisable avec sum
; aussi le besoin de prendre en compte les valeurs négatives ou nulles est un peu contre-intuitif pour moi, alors que vous voulez simplement ajouter des valeurs composante par composante.
Je pense donc que ce serait une bonne idée d'écrire une classe personnalisée pour cela. C'était aussi l'idée de John Mutuma. Cependant, je veux ajouter ma solution :
Je crée une classe qui se comporte très similairement à un dict
, passant essentiellement tous les appels de membres à la _data
sous-jacente dans la méthode getatrr. Les seules deux choses qui sont différentes sont :
- elle a une
DEFAULT_VALUE
(similaire à collections.defaultdict
) qui est utilisée comme valeur pour les clés inexistantes.
-
elle implémente une méthode __add__()
qui (avec la méthode __radd__()
) se charge d'ajouter les dictionnaires composante par composante.
from typing import Union, Any
class AddableDict:
DEFAULT_VALUE = 0
def __init__(self, data: dict) -> None:
self._data = data
def __getattr__(self, attr: str) -> Any:
return getattr(self._data, attr)
def __getitem__(self, item) -> Any:
try:
return self._data[item]
except KeyError:
return self.DEFAULT_VALUE
def __repr__(self):
return self._data.__repr__()
def __add__(self, other) -> "AddableDict":
return AddableDict({
key: self[key] + other[key]
for key in set(self.keys()) | set(other.keys())
})
def __radd__(
self, other: Union[int, "AddableDict"]
) -> "AddableDict":
if other == 0:
return self
De cette façon, nous pouvons ajouter deux de ces objets et des itérables de ces objets ainsi que sum
:
>>> alpha = AddableDict({"a": 1})
>>> beta = AddableDict({"a": 10, "b": 5})
>>> alpha + beta
{'a': 11, 'b': 5}
>>> sum([beta]*10)
{'a': 100, 'b': 50}
À mes yeux, cette solution a l'avantage de fournir une interface simple et compréhensible pour le développeur à utiliser. Bien sûr, vous pouvez aussi hériter de dict
au lieu d'utiliser la composition.