How can I choose a custom string representation for a class itself (not instances of the class)?

Question:

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?


See How to print instances of a class using print()? for the corresponding question about instances of the class.

In fact, this question is really a special case of that one – because in Python, classes are themselves also objects belonging to their own class – but it’s not directly obvious how to apply the advice, since the default "class of classes" is pre-defined.

Asked By: Björn Pollex

||

Answers:

class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"
Answered By: Andrey Gubarev

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.

Edit: Python 3 Version

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

class C(object, metaclass=MC):
    pass


print(C)

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
Answered By: user1767754

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!

Answered By: Jonathan Eunice

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.
Answered By: Aviv Goll

Because you need a metaclass to do this, but you need the metaclass itself to have a parameter, you can do it with a metaclass that captures the name via lexical scope.

I find this a bit easier to read / follow than some of the alternatives.


class type_: pass

def create_type(name):
    # we do this so that we can print the class type out
    # otherwise we must instantiate it to get a proper print out
    class type_metaclass(type):
        def __repr__(self):
            return f'<{name}>'

    class actual_type(type_, metaclass=type_metaclass):
        pass
    return actual_type

my_type = create_type('my_type')

print(my_type)
# prints "<my_type>"
Answered By: Rebs

Another answer, with:

  • decorator
  • types (so you keep auto-complete in IDEs)
  • works as of v3.10
import typing


class ClassReprMeta(type):
    def __repr__(self):
        attrs_str = ", ".join(
            f"{key}={getattr(self, key)}"
            for key in dir(self)
            if not key.startswith("_")
        )

        return f"{self.__name__}({attrs_str})"


T = typing.TypeVar("T")


def printable_class(cls: T) -> T:
    """Decorator to make a class object printable"""
    return ClassReprMeta(cls.__name__, cls.__bases__, dict(cls.__dict__))


@printable_class
class CONFIG:
    FIRST = 1
    SECOND = 2


print(CONFIG)  # CONFIG(FIRST=1, SECOND=2)
Answered By: David Gilbertson
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.