user7084210
user7084210

Reputation: 71

How do I use __repr__ with multiple arguments?

(Python 3.5.2)

I have defined __repr__ for one of my classes as follows:

class d():
    def __init__(self):
        self._values = []
        return
    def __pos__(self):
        return self._values[0]
    def __repr__(self, value):
        self._values.append(value)
        return

Now I want

x = d()
print(x,"a")
print(+x)

to return

a

but instead I get

TypeError: __repr__() missing 1 required positional argument: 'value'

I've tried a few variations such as print(x),"a" with no luck.

Upvotes: 3

Views: 9903

Answers (3)

chepner
chepner

Reputation: 530843

If you want to control how an instance of your class is displayed, the right way to do that is to override the __format__ method. In general, the three methods you can override are used to:

  1. __repr__ - used when the object needs to be displayed in the interactive interpreter, usually as a debugging aid. As far as possible, it should be a string that could recreate the object if evaluated.
  2. __str__ - used when the object is passed to str or called when print attempts to display your object. Without another definition, it simply calls __repr__. This is the "default" string representation for an instance.
  3. __format__ - used when your object is an argument to str.format. It receives as an additional argument the format specification (if any) that appears after the optional : in a replacement field.

Here is a simple example of a class to represent pairs of numbers. The character used to separate the numbers can be configured via the format specification.

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

    def __format__(self, spec):
        return "{}{}{}".format(self.x, spec, self.y)

    def __str__(self):
        return "{:/}".format(self)

    def __repr__(self):
        return "Pair({}, {})".format(self.x, self.y) 

It can be used as follows:

>>> x = Pair(1,2)
>>> x  # using __repr__
Pair(1, 2)
>>> str(x)   # using __str__, with a default separator of /
'1/2'
>>> print(x)  # uses __str__ implicitly
1/2
>>> "{}".format(x)  # no separator specified
'12'
>>> "{:-}".format(x)  # use - to separate the two numbers
'1-2'

Note that in the case of format, the spec is not necessarily part of the return value, but acts as an instruction on how to format the value.

Upvotes: 22

QuantumChris
QuantumChris

Reputation: 1093

There are a few people saying you should never add arguments to __str__ or __repr__ but I disagree.

For example, if you want to display the string representation of a class's objects you would write a custom __str__. But what if some of the objects were really long? Sometimes you may want to truncate those objects, sometimes you may want them in full.

class Compute:

    def __init__(self, foo, ...):
        self.foo = foo
        ...

    def __str__(self, trunc=False):
        return_str = ''
        for item in self.__dict__:
            if trunc:
                # If the length of the str representation of the item is long, shorten it.
                return_str += f'\n{item} = {self.__dict__[item] if len(str(self.__dict__[item] < 50) else str(self.__dict__[item])[:51]}'
            else:
                # This would be whatever your usual __str__ method returns
                return_str += f'\n{item} = {self.__dict__[item]}'

        return return_str

You would call this simply using:

bar = Compute(foo=...)

print(bar.__str__(trunc=True))

Now, if your __init__ contains any objects whose length as a string is particularly long, (say you've got a large list of data) it will only print up to the 51st character.

You can add a lot more logic to how you want the truncated strings to be printed too.

I would recommend having default arguments set to False in the dunder method. This means that unless explicitly asked for, the __str__ method can behave normally.

I have used this when I wanted to print a class's objects both to the terminal and to a log file. I only wanted them printed in full to the log file, and a truncated version to the terminal so I wrote something similar to this.

Hope this helps.

Upvotes: 12

Chris
Chris

Reputation: 22953

Your using __repr__ the wrong way. __repr__ should be used to return a representation of your object in a printable, formatted way. As opposed to Python simply printing the name and memory address of your object. Per the documentation of __repr__:

Called by the repr() built-in function to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned. The return value must be a string object. If a class defines __repr__() but not __str__(), then __repr__() is also used when an “informal” string representation of instances of that class is required.

I'm really not sure what your trying to achive with using __repr__ though. If you simply want add an element to self.values via a method of d, ditch the magic methods and just create your own:

class d():
    def __init__(self):
        self._values = []

    def __pos__(self):
        return self._values[0]

    def append(self, value): # create your won function
        self._values.append(value)

Upvotes: 2

Related Questions