1

I have a dictionary which holds objects in its values. I would like to use the str function on the dictionary, so that it will recursively run on all the other objects in the dictionary.

For instance, I have the following object:

>>> class Temp(object):
...     def __init__(self):
...         self._item = 8
...     def __str__(self):
...         return str(self._item)
... 
>>> t = Temp()
>>> str(t)
'8'

I create a dictionary and put the object in it:

>>> dict = {}
>>> dict['object'] = t

Now, when I use the str function on the dictionary I get the following:

>>> str(dict)
"{'object': <__main__.Temp object at 0x7fa08889f910>}"

But I would like to get this:

>>> str(dict)
"{'object': 8}"
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
nerez
  • 437
  • 4
  • 18

4 Answers4

6

Perhaps you want a dict subclass:

class strdict(dict):
  def __str__(self):
    return "{%s}"%', '.join("%r: %s"%p for p in self.items())

Consider using iteritems in Python 2.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
3

dict, like most other built-in container classes, uses the __repr__ of an object when stringifying itself. While a missing __str__ will fail over to __repr__, the reverse is not true: __repr__ does not fail over to __str__.

The simplest way to obtain the result you want is to rename __str__ to __repr__:

class Temp(object):
    def __init__(self):
        self._item = 8
    def __repr__(self):
        return str(self._item)

print({'object': Temp()})

results in

{'object': 8}

At the same time, the normal string representation will be as before because of the failover:

print(Temp())

results in

8

Keep in mind that print calls str on its positional arguments.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • Correct, but making the `repr` of your class be either string representation of its wrapped object is a bit misleading. – Davis Herring Dec 24 '18 at 05:28
  • @Davis. Why? What do you think a scalar numpy array does? – Mad Physicist Dec 24 '18 at 05:33
  • Because `repr` is supposed to be `eval` to an equal object (or be obviously not `eval`lable at all). – Davis Herring Dec 24 '18 at 05:37
  • @Davis. it's not really supposed to, and generally doesn't, although for many simple containers it happens to. Repr is just supposed to be a meaningful representation of the state. If the entire purpose of the object is to be a wrapper for the item it contains, this sort of repr is fine. +1 to your answer of course though. – Mad Physicist Dec 24 '18 at 05:40
  • @Davis. In context of your answer, I see your point. In a sense, this answer explains why OP's expectation does not pan out, while yours provides a correct workaround. – Mad Physicist Dec 24 '18 at 05:43
  • 1
    There are guidelines in the docs: _If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned._ – VPfB Dec 24 '18 at 07:34
0

This approach might help. Instead of assigning 't' directly like you have done below.

>>> dict['object'] = t

You can typecast it and then save it into the dictionary. Like this

>>> dict['object'] = str(t)

Then, the dictionary will have the value you require.

>>> str(dict)
"{'object': '8'}"
Anurag A S
  • 725
  • 10
  • 23
0

If you do not mind iterating over the dict, we can write a replacement for str:

def nerez_str(d):
  new_d = {}
  for k, v in d.items():
    new_d[str(k)] = str(v)
  return new_d

now instead of calling str on your dict, pass it to nerez_str.

We may make the function recursive, if you are expecting your dictionary to contain other dictionaries as values.

Anuvrat Parashar
  • 2,960
  • 5
  • 28
  • 55