0

For a long time I have been puzzled by Alex Martelli's remark about:

(...) the fuzzy unattainable goal of making repr's returned value acceptable as input to eval!

So I gave it a try and came up with this:

class Sic():
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.method_var = lambda x, y, z : x + y + z

    def __repr__(self):
         def right_quotes(value):
             return  repr(value).translate(str.maketrans('\'\"', '\"\''))
         from inspect import signature
         class_sig = signature(self.__class__)    
         fields = tuple('{}={}'.format(k,right_quotes(v)) for k,v  in self.__dict__.items() if k in class_sig.parameters)
         return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'","")

Is this a correct general implementation of __repr__? If not could you give an example where it fails?

(I have improved the original version with the suggestion of Barmar, and responding to the objection of Kuco 23. I am looking here to a most general solution, even if it involves using introspection.)

Javier Ruiz
  • 410
  • 4
  • 10

1 Answers1

1

What the quote means is that, when a string returned from the __repr__ method is ran on a python interpreter, it should evaluate to the object at its initialization stage.

The code you provided has a couple of faults. Any object encoded in the __repr__ return string, should also be represented with their __repr__ method. And also the self.__dict__.items() will return (name, value) pair for every attribute name set to the object self. The problem here is that some of those object were not used for the self's initialization. For example if your code was modified as

class Sic():
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
        self.method_var = someFunction(x, y, z)

    def __repr__(self):
         fields = tuple("{}={}".format(k, v) for k, v in self.__dict__.items())
         return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'","")

the repr method would return Sic(x=x0, y=y0, z=z0, method_var=mv0), even though that string's evaluation would be invalid, as the __init__ method only takes 3 arguments.

The safest option would be to implement the __repr__ method for any class you implement separately, as in

class Sic():
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def __repr__(self):
         args = map(repr, (self.x, self.y, self.z))
         return f"Sic({', '.join(args)})"

If you insist on defining a __repr__ method for a custom class, you would have to know for each object, which arguments the __init__ method takes in, which would probably require some additional modifications to every class and make the code more complex.

kuco 23
  • 786
  • 5
  • 18