How to Create a Custom String Representation for a Class Object?

How to Create a Custom String Representation for a Class Object?

Consider this class:

class foo(object):
    pass

The default string representation looks something like this:

>>> str(foo)
"<class '__main__.foo'>"

How can I make this display a custom string?

5 Answers

Implement __str__() or __repr__() in the class's metaclass.

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object):
  __metaclass__ = MC

print(C)

Use __str__ if you mean a readable stringification, use __repr__ for unambiguous representations.

7
class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"
3

If you have to choose between __repr__ or __str__ go for the first one, as by default implementation __str__ calls __repr__ when it wasn't defined.

Custom Vector3 example:

class Vector3(object):
    def __init__(self, args):
        self.x = args[0]
        self.y = args[1]
        self.z = args[2]

    def __repr__(self):
        return "Vector3([{0},{1},{2}])".format(self.x, self.y, self.z)

    def __str__(self):
        return "x: {0}, y: {1}, z: {2}".format(self.x, self.y, self.z)

In this example, repr returns again a string that can be directly consumed/executed, whereas str is more useful as a debug output.

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3
3

Ignacio Vazquez-Abrams' approved answer is quite right. It is, however, from the Python 2 generation. An update for the now-current Python 3 would be:

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object, metaclass=MC):
    pass

print(C)

If you want code that runs across both Python 2 and Python 3, the six module has you covered:

from __future__ import print_function
from six import with_metaclass

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(with_metaclass(MC)):
    pass

print(C)

Finally, if you have one class that you want to have a custom static repr, the class-based approach above works great. But if you have several, you'd have to generate a metaclass similar to MC for each, and that can get tiresome. In that case, taking your metaprogramming one step further and creating a metaclass factory makes things a bit cleaner:

from __future__ import print_function
from six import with_metaclass

def custom_class_repr(name):
    """
    Factory that returns custom metaclass with a class ``__repr__`` that
    returns ``name``.
    """
    return type('whatever', (type,), {'__repr__': lambda self: name})

class C(with_metaclass(custom_class_repr('Wahaha!'))): pass

class D(with_metaclass(custom_class_repr('Booyah!'))): pass

class E(with_metaclass(custom_class_repr('Gotcha!'))): pass

print(C, D, E)

prints:

Wahaha! Booyah! Gotcha!

Metaprogramming isn't something you generally need everyday—but when you need it, it really hits the spot!

Just adding to all the fine answers, my version with decoration:

from __future__ import print_function
import six

def classrep(rep):
    def decorate(cls):
        class RepMetaclass(type):
            def __repr__(self):
                return rep

        class Decorated(six.with_metaclass(RepMetaclass, cls)):
            pass

        return Decorated
    return decorate


@classrep("Wahaha!")
class C(object):
    pass

print(C)

stdout:

Wahaha!

The down sides:

  1. You can't declare C without a super class (no class C:)
  2. C instances will be instances of some strange derivation, so it's probably a good idea to add a __repr__ for the instances as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Marcus Vance
Author

Marcus Vance

Marcus Vance is a cybersecurity auditor and technology writer dedicated to educating the public about online safety, data privacy regulations, enterprise security, and emerging cyber threats.