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.
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)
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
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
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:
You can't declare C without a super class (no class C:)
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.
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>"
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)
Related
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.
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)
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
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
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:
You can't declare C without a super class (no class C:)
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.
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>"
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)
I have a class like:
class MyClass:
Foo = 1
Bar = 2
Whenever MyClass.Foo or MyClass.Bar is invoked, I need a custom method to be invoked before the value is returned. Is it possible in Python? I know it is possible if I create an instance of the class and I can define my own __getattr__ method. But my scnenario involves using this class as such without creating any instance of it.
Also I need a custom __str__ method to be invoked when str(MyClass.Foo) is invoked. Does Python provide such an option?
__getattr__() and __str__() for an object are found on its class, so if you want to customize those things for a class, you need the class-of-a-class. A metaclass.
class FooType(type):
def _foo_func(cls):
return 'foo!'
def _bar_func(cls):
return 'bar!'
def __getattr__(cls, key):
if key == 'Foo':
return cls._foo_func()
elif key == 'Bar':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return 'custom str for %s' % (cls.__name__,)
class MyClass(metaclass=FooType):
pass
# # in python 2:
# class MyClass:
# __metaclass__ = FooType
print(MyClass.Foo)
print(MyClass.Bar)
print(str(MyClass))
printing:
foo!
bar!
custom str for MyClass
And no, an object can't intercept a request for a stringifying one of its attributes. The object returned for the attribute must define its own __str__() behavior.
Updated 2023-02-20 for Python 3.x default implementation (python 2 as a comment).
(I know this is an old question, but since all the other answers use a metaclass...)
You can use the following simple classproperty descriptor:
class classproperty(object):
""" #classmethod+#property """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Use it like:
class MyClass(object):
#classproperty
def Foo(cls):
do_something()
return 1
#classproperty
def Bar(cls):
do_something_else()
return 2
For the first, you'll need to create a metaclass, and define __getattr__() on that.
class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
For the second, no. Calling str(MyClass.Foo) invokes MyClass.Foo.__str__(), so you'll need to return an appropriate type for MyClass.Foo.
Surprised no one pointed this one out:
class FooType(type):
#property
def Foo(cls):
return "foo!"
#property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Works:
>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'
(for Python 2.x, change definition of MyClass to:
class MyClass(object):
__metaclass__ = FooType
)
What the other answers say about str holds true for this solution: It must be implemented on the type actually returned.
Depending on the case I use this pattern
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Then you import and use it.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
This avoid use of metaclass, and handle some use cases.
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.
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)
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
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
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:
You can't declare C without a super class (no class C:)
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.
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>"
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)
I would like to create a class in Python that manages above all static members. These members should be initiliazed during definition of the class already. Due to the fact that there will be the requirement to reinitialize the static members later on I would put this code into a classmethod.
My question: How can I call this classmethod from inside the class?
class Test():
# static member
x = None
# HERE I WOULD LOVE TO CALL SOMEHOW static_init!
# initialize static member in classmethod, so that it can be
#reinitialized later on again
#classmethod
def static_init(cls):
cls.x = 10
Any help is appreciated!
Thanks in advance,
Volker
At the time that x=10 is executed in your example, not only does the class not exist, but the classmethod doesn't exist either.
Execution in Python goes top to bottom. If x=10 is above the classmethod, there is no way you can access the classmethod at that point, because it hasn't been defined yet.
Even if you could run the classmethod, it wouldn't matter, because the class doesn't exist yet, so the classmethod couldn't refer to it. The class is not created until after the entire class block runs, so while you're inside the class block, there's no class.
If you want to factor out some class initialization so you can re-run it later in the way you describe, use a class decorator. The class decorator runs after the class is created, so it can call the classmethod just fine.
>>> def deco(cls):
... cls.initStuff()
... return cls
>>> #deco
... class Foo(object):
... x = 10
...
... #classmethod
... def initStuff(cls):
... cls.x = 88
>>> Foo.x
88
>>> Foo.x = 10
>>> Foo.x
10
>>> Foo.initStuff() # reinitialize
>>> Foo.x
88
You call a class method by appending the class name likewise:
class.method
In your code something like this should suffice:
Test.static_init()
You could also do this:
static_init(Test)
To call it inside your class, have your code do this:
Test.static_init()
My working code:
class Test(object):
#classmethod
def static_method(cls):
print("Hello")
def another_method(self):
Test.static_method()
and Test().another_method() returns Hello
You can't call a classmethod in the class definition because the class hasn't been fully defined yet, so there's nothing to pass the method as its first cls argument...a classic chicken-and-egg problem. However you can work around this limitation by overloading the __new__() method in a metaclass, and calling the classmethod from there after the class has been created as illustrated below:
class Test(object):
# nested metaclass definition
class __metaclass__(type):
def __new__(mcl, classname, bases, classdict):
cls = type.__new__(mcl, classname, bases, classdict) # creates class
cls.static_init() # call the classmethod
return cls
x = None
#classmethod
def static_init(cls): # called by metaclass when class is defined
print("Hello")
cls.x = 10
print Test.x
Output:
Hello
10
After re-reading your question carefully this time I can think of two solutions. The first one is to apply the Borg design pattern. The second one is to discard the class method and use a module level function instead. This appears to solve your problem:
def _test_static_init(value):
return value, value * 2
class Test:
x, y = _test_static_init(20)
if __name__ == "__main__":
print Test.x, Test.y
Old, incorrect answer:
Here's an example, I hope it helps:
class Test:
x = None
#classmethod
def set_x_class(cls, value):
Test.x = value
def set_x_self(self):
self.__class__.set_x_class(10)
if __name__ == "__main__":
obj = Test()
print Test.x
obj.set_x_self()
print Test.x
obj.__class__.set_x_class(15)
print Test.x
Anyway, NlightNFotis's answer is a better one: use the class name when accessing the class methods. It makes your code less obscure.
This seems like a reasonable solution:
from __future__ import annotations
from typing import ClassVar, Dict
import abc
import string
class Cipher(abc.ABC):
#abc.abstractmethod
def encrypt(self, plaintext: str) -> str:
pass
#abc.abstractmethod
def decrypt(self, ciphertext: str) -> str:
pass
class RotateCipher(Cipher, abc.ABC):
#staticmethod
def rotate(n: int) -> str:
return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]
class VigenereCipher(RotateCipher):
_TABLE: ClassVar[Dict[str, str]] = dict({(chr(i + ord("A")), RotateCipher.rotate(i)) for i in range(26)})
def encrypt(self, plaintext: str) -> str:
pass
def decrypt(self, plaintext: str) -> str:
pass
vc = VigenereCipher()
The method is now a static method of the cipher, nothing outside the classes is referenced. You could opt to name RotateCipher _RotateCipher instead, if you don't want people using it by itself.
Note: I removed the Final, as I ran this on 3.7, but after reading the documentation on Final, I don't think it would affect the solution? Also added an import for string which the question was missing. And finally added an implementation for the abstract methods, alternatively, could have let VigenereCipher inherit from abc.ABC as well.
If your classmethod is not used very often do a lazy evaluation
class A() {
# this does not work: x=A.initMe()
#classmethod
def initMe(cls) {
if not hasattr(cls,"x"):
# your code her
cls.x=# your result
pass
#classmethod
def f1(cls) {
# needs initMe
cls.initMe()
# more code using cls.x
}
}
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.
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)
class foo(object):
def __str__(self):
return "representation"
def __unicode__(self):
return u"representation"
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
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:
You can't declare C without a super class (no class C:)
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.
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>"
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)