1

I have some dictionaries that I use in a package of mine that could easily be pandas.Seriess. However, I leave them as dicts simply because of the .update method, which both changes existing values and adds a new values to the dict in place. The pandas.Series.update only changes values in place. So I am forced to use dicts (I think at least).

The drag is that dicts are hard to read when you print them with the print function, so I'd like to print them more nicely for the user. The only way I know how to do this is creating a function solely for printing dicts, which I'd like to avoid. Is there someway that I can inherently change dicts so that, after running some command, every dict is printed more nicely? Maybe even quickly convert them into a pandas Series just for printing, since my dicts are very small. Something like

def _print_dict(self):
    print(pd.Series(self))

and then I could throw this somewhere such as

dict.__print_method__ = _print_dict

if __print_method__ were the method that the print function calls when printing dicts.

Anton Protopopov
  • 30,354
  • 12
  • 88
  • 93
TomCho
  • 3,204
  • 6
  • 32
  • 83
  • 3
    I'm not experienced with these stuff so I'll just post this as a comment: As far as I know, you are looking for `__str__` method. If you define a new class that inherits from dict, you can change its `__str__` method to `def __str__(self): return pd.Series(self).__str__()` Although it may be unnecessary to convert it to a Series. Maybe looking up the method and modifying for dict could be more efficient. – ayhan Jun 09 '16 at 13:44
  • You're right, that is the correct method. I don't know how to modify or replace it, though. I tried just doing `dict.__str__ = __str__`, after your definition, but apparently I can't set attributes of dict types. I'm also not sure what you mean by new class that inherits from dict. – TomCho Jun 09 '16 at 13:55
  • Try [this answer](http://stackoverflow.com/a/10493071/5276797). – IanS Jun 09 '16 at 13:59
  • @TomCho Since dict is a builtin type I thought that it might be necessary to copy that type like `class dict2(dict):` and just change the `__str__` method but unutbu's answer is better as always. :) – ayhan Jun 09 '16 at 14:27
  • @ayhan unutbu's answer is pretty good, indeed, but I would rather not change the print function itself. It seems like a much more "invasive" procedure. Am I wrong to think that? – TomCho Jun 09 '16 at 15:12

1 Answers1

3

In Python3, print is a function. In Python2.7, print is a statement, but if you declare from __future__ import print_function at the top of the script, then print becomes a function. It is possible redefine the function:

from __future__ import print_function
import pandas as pd
try: import builtins
except ImportError: import __builtin__ as builtins

def print(*args, **kwargs):
    args = [item if not isinstance(item, (dict,)) else pd.Series(item)
            for item in args]
    builtins.print(*args, **kwargs)


print({'cheese':'stilton', 'swallow':'african', 'ride':'coconuts'})
print(2,3,4, sep=' -- ')

yields

cheese      stilton
ride       coconuts
swallow     african
dtype: object
2 -- 3 -- 4

By the way, dict is defined in C. In particular its __repr__ is defined in C. When the __str__ method is empty, the __repr__ is used by default to generate a string representation of the object.

The special methods of C-defined objects, such as __str__ or __repr__ can not be monkeypatched:

dct = {'cheese':'stilton', 'swallow':'african', 'ride':'coconuts'}
def mystr(self):
    return str(pd.Series(self))

dct.__str__ = mystr

raises

AttributeError: 'dict' object attribute '__str__' is read-only

If dct were an instance of a Python-defined class, then the story would be different. The special methods of such classes can be defined, overridden or monkeypatched:

import pandas as pd
class MyDict(dict):
    def __str__(self):
        return str(pd.Series(self))

dct = {'cheese':'stilton', 'swallow':'african', 'ride':'coconuts'}
mydct = MyDict(dct)
print(mydct)

yields

cheese      stilton
ride       coconuts
swallow     african
dtype: object

But to use this would require changing all your dicts to MyDicts. That's much more difficult than redefining the print function.

(By the way, beware that there are hidden pitfalls to subclassing dict properly. For example, if you redefine __setitem__ but not update, then update will not call your __setitem__ method.)

Community
  • 1
  • 1
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • I see you have improved your answer. Please check out my latest comment to my own question. I feel like "patching up" the print function is a rather invasive solution for a simple question. But then again, I'm not a professional programmer. May I ask what is your opinion? – TomCho Jun 09 '16 at 15:14
  • Changing the `print` function is invasive. Monkeypatching `dict.__str__` would also be invasive -- if it were even possible, which it is not. The only other solution that I can think of requires changing every dict to a `MyDict`. That can be very difficult to get right, especially if other functions (maybe even functions you do not control) return dicts. Overall, I suspect using `MyDict` would require far more invasive changes to your script than redefining `print`. So whether it's invasive or not, I don't think there is a better option *to achieve your stated goal* than redefining `print`. – unutbu Jun 09 '16 at 15:29
  • I understand. The trouble with MyDict (which would be my preferred approach) is not even the fact that I should change from `dict` to `MyDict` (this is not that hard from the way I set up my code). It is the fact that many dict (and therefore MyDict) methods are still going to return a dict. For example the `copy` method. Given everything you said, I might be better off with a different approach, maybe using `pandas.Series` instead of a dict or MyDict and defining a method to it that works as the `update` method for dict. – TomCho Jun 09 '16 at 15:56