Search code examples
pythonnamedtuplerepr

Bring namedtuple's __str__ and __repr__ behavior to regular classes


https://mail.python.org/pipermail/python-ideas/2014-September/029310.html

I always thought namedtuple builtin __str__ and __repr__ were very neat and I'm looking for a simple way to apply it to any classes of mine easily.

>>> from collections import namedtuple
>>> A  = namedtuple("A", ["foo"])
>>> print(A(foo=1))
A(foo=1)
>>> str(A(foo=1))
'A(foo=1)'
>>> repr(A(foo=1))
'A(foo=1)'

EDIT:

I initially started with a bunch of lengthy, not dynamic, hardcoded __repr__. I don't like that. namedtuple does it fancy and automatically.

def __repr__(self):
    return 'className(attrA={attrA}, attrB={attrB})'.format(**vars(self)))

Solution

  • A bit hacky but this does it:

    from collections import namedtuple
    
    def nice_repr(obj):
        def nice_repr(self):
            return repr(
                namedtuple(
                    type(self).__name__,
                    vars(self)
                )(**vars(self))
            )
    
        obj.__repr__ = nice_repr
    
        return obj
    

    Example:

    @nice_repr
    class A:
        def __init__(self, b, c):
            self.b = b
            self.c = c
    
    print(repr(A(1, 2)))  # Outputs: A(c=2, b=1)
    

    EDIT: (Fail-safe version)

    def nice_repr(obj):
        """ Decorator to bring namedtuple's __repr__ behavior to regular classes. """
    
        def nice_repr(self):
            v = vars(self)
    
            # Prevent infinite looping if `vars` happens to include `self`.
            del(v['self'])
    
            return repr(namedtuple(type(self).__name__, v)(**v))
    
        obj.__repr__ = nice_repr
    
        return obj