0

Consider:

class Base:
    def __init__(self):
        self.__repr__ = lambda: "String!"

class Child(Base):
    pass

c = Child()
print(f"{c}")
print(f"{c.__repr__()}")

This results in:

<__main__.Child object at 0x7f07cd88f850>
String!

I get the same output when changing the __str__ and __format__ methods. I would like for Child's representation in an f-string to be just "String!", but changing __str__, __repr__, and __format__ doesn't achieve this.

What does Python use to determine what's displayed in an f-string if not the instance's __repr__ or either of the other two methods?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
AmagicalFishy
  • 1,249
  • 1
  • 12
  • 36
  • 1
    @jonrsharpe When I say I've tried changing `__repr__`, it's because I've tried changing `__repr__`. Let me post a fuller example in case there's something else happening here (but, fwiw, while I get that example going, doing `Foo().__repr__()` gives me the expected result while printing it in an f-string does not). – AmagicalFishy Apr 08 '22 at 18:35
  • 5
    `self.__repr__ = lambda: self.type_` **does not create a method**. For starters, dunder methods are looked up directly on a class. – juanpa.arrivillaga Apr 08 '22 at 18:47
  • 3
    By default, string formatting uses `str`, but you can use format specifiers, `f"{my_obj!r}"` for the `repr` – juanpa.arrivillaga Apr 08 '22 at 18:48
  • @juanpa.arrivillaga Would you mind putting this in an answer along with e.g. a link to the docs; I'll accept it if you do—that's exactly the info. I needed! I (obviously) had no idea that magic methods couldn't be created/altered that way, especially given that `body.__repr__()` seems to work as intended, but isn't actually considered by Python to be the magic `__repr__()` method – AmagicalFishy Apr 08 '22 at 18:51
  • @AmagicalFishy it works because you are calling it directly, it is just a function. It is a *hook* used by the runtime, but in a certain way. It is required to live in the class namespace – juanpa.arrivillaga Apr 08 '22 at 18:55
  • @juanpa.arrivillaga That makes sense. Thank you! (Just to reiterate, if you post an answer I'll accept it) – AmagicalFishy Apr 08 '22 at 18:57
  • Feel free to answer and accept your own question! – juanpa.arrivillaga Apr 08 '22 at 19:01
  • @juanpa.arrivillaga Will do. Thanks again for the info. :) – AmagicalFishy Apr 08 '22 at 19:04
  • 4
    At this point, the f-string is actually redundant, i.e. `print(f"{c}")` could just as well be `print(c)` – wjandrea Apr 08 '22 at 19:21

1 Answers1

2

Python magic methods (i.e., methods like __str__, __repr__, and __format__) are looked up on the object's type, not the object itself (see: Special method lookup), and doing something like self.__repr__ = lambda: "String!" changes the dictionary of the instance (not the class itself). Given the above example, c.__dict__ results in:

{'__repr__': <function Base.__init__.<locals>.<lambda> at 0x7f07cd81de10>}

That is, we've changed the dictionary of the instance to include __repr__, but haven't actually changed the instance's special function (which is looked up on the class itself, not any individual instance of said class). So when the instance is represented on an f-string, the special function is used. The correct thing to do would be something like:

class Base:
    def __repr__(self):
        return "String!"


class Child(Base):
    pass

c = Child()
print(f"{c}")

Which outputs:

String!
AmagicalFishy
  • 1,249
  • 1
  • 12
  • 36