0

I want to assign comments to the key-value pairs in my dictionary, ideally in a way that I can access them directly. Since I'm new to manipulating classes, I only came this far:

class MyDict(dict):
    def __init__(self, *arg, **kwargs):
        super(MyDict, self).__init__(*arg, **kwargs)

mydict = MyDict()
mydict['first'] = 1
mydict['second'] = 2

Is there a way to add a comment to each of the key-value pairs so I can access them like

for key, value, comment in mydict.iteritems():
    print(key, value, comment)

or

for key, value in mydict.iteritems():
    print(key, value, key.comment)  # or value.comment

Any other way is also fine, as long as the comments are directly accessible (meaning not via indexing of lists or so).

s6hebern
  • 725
  • 9
  • 18
  • Would have thought the most natural way would be to have a tuple or named tuple as the dictionary value associated with each key. – ChrisOram Jan 19 '22 at 09:42
  • in theory you can overload all dictionary functions to get it to work, but it will be much slower than just storing a tuple, because dictionary functions are implemented in C. – Ahmed AEK Jan 19 '22 at 10:01

1 Answers1

1

If you store (value, comment) pairs, for each pair you can use tuple unpacking in the for statement (note the parenthesis). Although, this is kind of contradicting your "non-indexing" request:


mydict[key] = (value, comment)

for key, (value, comment) in mydict.items():
    print(key, value, comment)

Edit: Following specifications in the comments:

You could subclass a dict, that has another dict containing the comments as a member. Every regular dictionary method is left as is, but a specific method is implemented to iterate over the items along with a comment. Comments are added by using the corresponding method add_comment. If you want to include the comments into the initialzation, while keeping full compatibility with regular dictionary construction more work needs to be put into the __init__ definition.

class MyCommentedDict(dict):
  def __init__(self, *args, **kwargs):
    super(MyCommentedDict, self).__init__(*args, **kwargs)
    self._comments = dict()

  def add_comment(self, key, comment):
    self._comments.update({key:comment})
  
  def commented_items(self):
    """ Returns a generator that yields triplets of (key, value, comment).
    For all entries without a provided comment `None` is returned as comment."""
    return ((key, value, self._comments.get(key, None))
             for key, value in self.items())

a_dict = MyCommentedDict({'color': 'blue', 'fruit': 'apple', 'pet': 'dog'})
a_dict.add_comment('color', 'This a is color')

for key, value in a_dict.items():
  print(key, value)

>>>color blue
>>>fruit apple
>>>pet dog

for key, value, comment in a_dict.commented_items():
  print(key, value, comment)

>>>color blue This a is color
>>>fruit apple None
>>>pet dog None

Jonathan Weine
  • 655
  • 3
  • 11
  • This would practically work, true. But is there a way to alter the `dict` class in the described way? Ideally, I (and all others who might use it) don't want to care about indexing when working with the commented dict. – s6hebern Jan 19 '22 at 09:51
  • 1
    In that case look into `collections.abc`, you can inherit the base class that supports dictionary operations and implement your spec with those, see https://stackoverflow.com/a/21368848/15981783. – ChrisOram Jan 19 '22 at 10:13
  • @s6hebern, Is my understanding correct, that you want to have a datacontainer that behaves like a `dict` containing `(key, value)` - pairs in all cases *except* when iterating over the items, in which case you want to return `(key, value, comments)`? Or do you want to be able to have the original return on iteration as well? – Jonathan Weine Jan 19 '22 at 10:51
  • And additionaly, how to you intend to add the comments to each value? Are these already pairs on initialization? – Jonathan Weine Jan 19 '22 at 10:54
  • @JonathanWeine, thought about some additional function like `mydict['somekey'].add_comment('this is a comment')`. No need to do it directly on initialization. – s6hebern Jan 19 '22 at 12:09
  • 1
    So what should it return for alle keys wthout a comment? `None`? The cleanest option probably is subclassing as ChrisORam suggested especially if you want to stay consistent with all definitions of the `dict` class. An easier option could be to subclass a dict, that has another member dict containing `comments`, which is updated via the `add_comment` method. If that seems sufficient, I'll update the answer – Jonathan Weine Jan 19 '22 at 12:15
  • Sounds sufficient, yes, although I don't fully understand what you mean (yet). It probably gets clear when I see the code. Thanks already – s6hebern Jan 20 '22 at 06:22
  • Looks like a very nice solution, but I get `TypeError: dict.get() takes no keyword arguments`. (Python 3.9) – s6hebern Jan 20 '22 at 12:04
  • 1
    That shouldn't be a problem. The `default=None`, in `dict.get(...)` can be passed as positional argument then. I'll update the answer – Jonathan Weine Jan 20 '22 at 14:39