I was trying to override __str__
and __repr__
for class as shown in the code below. The new methods are called whenever I call the instance_method but the object calls for class_method remains the same (Please see the code snippet and the output below for clarity). Is there a way possible to override __str__
and __repr__
for @classmethod
so that the value of cls
can be changed?
I have also tried adding __str__
and __repr__
as @classmethod
but nothing changed.
class Abc:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Added {self.name}"
def __repr__(self):
return f"instance method repr"
def instance_method(self):
print(f"instance method {self}")
@classmethod
def __repr__(cls):
return f"class method"
@classmethod
def __str__(cls):
return f"class method"
@classmethod
def class_method(cls):
print(f"class method '{cls}'")
@staticmethod
def static_method():
print(f"static method")
def add(self, a: int,b: int,c: int) -> int:
return a+b+c
o = Abc("alpha")
o.class_method()
o.static_method()
o.instance_method()
Abc.static_method()
Abc.class_method()
print(o.add(1,2,3))
Output of the above code:
class method '<class '__main__.Abc'>'
static method
instance method class method
static method
class method '<class '__main__.Abc'>'
6
Python doesn't look for a __str__
on the class itself, just like it won't use __str__
set on an instance. This applies to all special methods, see Special method lookup in the Python datamodel:
For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.
In short, str(something)
does not use something.__str__()
, it essentially uses type(something).__str__(something)
(*) exactly because you wouldn't want the definition of __str__
on a class to break when you use str(class_object)
, where class_object.__str__()
doesn't have an instance to pass in as self
.
You'll have to define a metaclass, because that's the 'thing' that makes classes and is returned by type(class_object)
:
class MetaAbc(type):
def __repr__(cls):
return "__repr__ on the metaclass"
def __str__(cls):
return "__str__ on the metaclass"
class Abc(metaclass=MetaAbc):
def __init__(self, name):
self.name = name
def __str__(self):
return f"Added {self.name}"
def __repr__(self):
return "instance method repr"
The metaclass=MetaAbc
syntax tells Python to use MetaAbc
instead of just type
as metaclass of the Abc
class; now type(Abc)
returns MetaAbc
:
>>> type(Abc)
<class '__main__.MetaAbc'>
and MetaAbc.__repr__
and MetaAbc.__str__
are used when representing a class, or converting it to a string; the methods on the class are used when dealing with an instance:
>>> Abc
__repr__ on the metaclass
>>> print(Abc)
__str__ on the metaclass
>>> Abc('foo')
instance method repr
>>> print(Abc('foo'))
Added foo
The @classmethod
decorator does not put a method into a different namespace; class methods are normal attributes of a class and are simply bound differently. @classmethod
's are still accessible on the instance, for example, but will always be passed the class object, even when accessed via the instance:
>>> Abc.class_method()
class method '__str__ on the metaclass'
>>> Abc("foo").class_method()
class method '__str__ on the metaclass'
(*) Python uses descriptor binding to implement methods, classmethods and staticmethods. Special method lookups look up the function object directly by traversing the class hierarchy to avoid triggering the normal binding process, then bind them manually. So str(something)
translates to next(c.__dict__['__str__'] for c in type(something).__mro__ if '__str__' in c.__dict__).__get__(something, type(something))()
. That's a bit of a mouthful, for normal methods this can be simplified to type(something).__str__(something)
as that has the same effect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With