39

In Python, the object class serves as the root superclass for all the (new-style) classes. By default at least, applying str and repr to the "class instance" of any subclass of object produces the same result:

>>> class spam(object): pass
... 
>>> str(spam)
"<class '__main__.spam'>"
>>> str(spam) == repr(spam)

I would like to define a subclass of object, say fancyobject, that is identical to object in every way, except that applying str and repr to fancyobject itself produces different outputs:

>>> class ham(fancyobject): pass
...
>>> str(ham)
'ham'
>>> repr(ham)
"<class '__main__.ham'>"

Is there a way to do this in Python?

PS: I'm aware of the __str__ special method, but it is my understanding that if class A overrides __str__, then the overriding method is called only when str is called on instances of A, not when it is called on A itself. I.e.:

>>> class A(object):
...     def __str__(self):
...         return 'from new __str__: ' + object.__str__(self)
... 
>>> str(A())
'from new __str__: <__main__.A object at 0x7f79c62a5310>'
>>> str(A)
"<class '__main__.A'>"
user
  • 5,370
  • 8
  • 47
  • 75
kjo
  • 33,683
  • 52
  • 148
  • 265

3 Answers3

44

Actually the same mechanism as for object instances applies for types. Types are just objects themselves, so they are converted to strings by calling the __str__() method on their type, which is called the "metaclass". So you have to overwrite the __str__() method on the metaclass:

class fancytype(type):
    def __str__(self):
        return self.__name__
class ham(object):
    __metaclass__ = fancytype
print ham

prints

ham
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Anyone got a pointer to how Python 3.3 and later (are supposed to) have changed that? – Jürgen A. Erhard Apr 14 '16 at 13:52
  • @JürgenA.Erhard At the time I wrote this (which was before the release of Python 3.3), the then current development version of Python had changed the result of `str(some_type)` to simply `some_type.__name__`. Apparently, this change never made it into the release, and I can't find any reference to it at the moment. I'll remove the comment from the answer. – Sven Marnach Apr 14 '16 at 16:12
7

You can also set the default metaclass for a whole module like this

class fancytype(type):
    def __str__(self):
        return self.__name__

__metaclass__ = fancytype

class ham:
    pass
print ham
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • Thanks, this is interesting, but it looks to me like it applies only to "classic" classes. I don't see how such technique could affect new-style classes (which inherit, directly or indirectly) from `object`. – kjo Nov 16 '11 at 03:42
  • 2
    @kjo: Well, the class `ham` in this example will automatically become a new-style class, because `fancytype` derives from `type`, which is the type of new-style classes. Everything that derives from `ham` will be a `fancytype` as well. – Sven Marnach Nov 16 '11 at 12:47
  • Thanks, that's good to know; until now I had thought that the only way to define a new-style classes was to explicitly include `object`, or some subclass of it, in the `(...)` clause of the `class ham(...):` statement. – kjo Nov 18 '11 at 16:21
4

Here's the new answer for Python 3. Basically, you pass in a metaclass as a keyword parameter to the class definition.

class fancytype(type):
    def __str__(self):
        return self.__name__
class ham(metaclass=fancytype):
    pass
print(ham)

prints

ham
Austin Cory Bart
  • 2,159
  • 2
  • 19
  • 32