Is there a __repr__() like method for a python class?

Question:

I’m solving a funny problem that requires to define a class that can be called like this:
class Chain(2)(3)(4)
And it should print out the multiplication of all arguments.
I ended up a solution like this:

class Chain():
    calc = 1 
    def __new__(cls, a=None):
        if a:
            cls.calc = cls.calc*a
            return cls
        else:
            return cls.calc

This works fine and self.calc is equal to 24 but i have a wrong representation <class '__main__.Chain'>.
Is there anyway to have representation of multiplication instead of class name like what we have in __repr__ for objects?

note: The call arguments count has no limits and may be different on each call.

Asked By: muradin

||

Answers:

First of all to answer your direct question from the title:

As everything in Python, classes are too – objects. And just like classes define how instances are created (what attributes and methods they will have), metaclasses define how classes are created. So let’s create a metaclass:

class Meta(type):
    def __repr__(self):
        return str(self.calc)

class Chain(metaclass=Meta):
    calc = 1

    def __new__(cls, a=None):
        if a:
            cls.calc = cls.calc*a
            return cls
        else:
            return cls.calc

print(Chain(2)(3)(4))

This will print, as expected, 24.


A few notes:

  1. Currently Meta simply accesses a calc attribute blindly. A check that it actually exists could be done but the code above was just to make the point.

  2. The way your class is implemented, you can just do Chain(2)(3)(4)() and you will get the same result (that’s based on the else part of your __new__).

  3. That’s a weird way to implement such behavior – you are returning the class itself (or an int…) from the __new__ method which should return a new object of this class. This is problematic design. A classic way to do what you want is by making the objects callable:

class Chain():
    def __init__(self, a=1):
        self.calc = a

    def __call__(self, a=None):
        if a:
            return self.__class__(self.calc * a)
        else:
            return self.calc

    def __repr__(self):
        return str(self.calc)

print(Chain(2)(3)(4))

This solves your problem of even needing to do what you want, because now you just implement the class’ __repr__ (because now each call in the chain returns a new object, and not the class itself).

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