{"id":"oop-techniques-repr","slug":"str-and-repr","title":"__str__ and __repr__","text":"A value can be shown as text in two ways. One is for people: readable text, with no extra marks. The other is for programmers: a **technical representation** that shows exactly what the value is. For a string, the difference is the quotes. The function `repr` gives the technical one:\n\n```pycon\n>>> name = \"Ada\"\n>>> print(name)\nAda\n>>> print(repr(name))\n'Ada'\n>>> print(repr(7))\n7\n>>> print([name, \"7\", 7])\n['Ada', '7', 7]\n```\n\nYou have seen the technical text many times without knowing it: a list prints its items with `repr`. That's why the strings in a printed list have quotes, and why `'7'` and `7` look different there. The interactive interpreter shows values with `repr` too.\n\n### __repr__ for your own classes\n\n`print`, `str` and f-strings use an object's `__str__`. The function `repr`, and a list being printed, use its **`__repr__`**. A good `__repr__` returns text that looks like the Python code that would create an object with the same contents:\n\n```python\nclass Order:\n    def __init__(self, table, dishes):\n        self.table = table\n        self.dishes = dishes\n\n    def __str__(self):\n        return f\"table {self.table}: {', '.join(self.dishes)}\"\n\n    def __repr__(self):\n        return f\"Order({self.table}, {self.dishes})\"\n\n\nfirst = Order(4, [\"soup\", \"pie\"])\nprint(first)\nprint(f\"Next up, {first}\")\nprint(repr(first))\norders = [first, Order(7, [\"tea\"])]\nprint(orders)\n```\n\n```output\ntable 4: soup, pie\nNext up, table 4: soup, pie\nOrder(4, ['soup', 'pie'])\n[Order(4, ['soup', 'pie']), Order(7, ['tea'])]\n```\n\nThe kitchen reads the `__str__` text, and a programmer looking at the list of orders sees exactly what each order holds. The list of dishes could go into `__repr__`'s f-string as it is, because a list always shows its items with `repr`: the dishes get their quotes by themselves. A string on its own doesn't. An f-string puts in its bare text, and `repr` adds the quotes:\n\n```pycon\n>>> dish = \"soup\"\n>>> print(f\"Order({dish})\")\nOrder(soup)\n>>> print(f\"Order({repr(dish)})\")\nOrder('soup')\n```\n\nSo when an attribute is a string, write `repr(...)` around it in `__repr__`, and the text reads like the code that makes the object.\n\nWithout a `__repr__`, a printed list of orders would show something like `<__main__.Order object at 0x7f3a2c1d5e50>` for each one: the class and an address in the computer's memory, which helps nobody.\n\n### When there's only __repr__\n\nIf a class has `__repr__` but no `__str__`, `print` uses `__repr__`. For a small class whose technical text reads well, that one method is enough for both:\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\n    def __repr__(self):\n        return f\"Point({self.x}, {self.y})\"\n\n\np = Point(2, 5)\nprint(p)\nprint([p, Point(0, 0)])\n```\n\n```output\nPoint(2, 5)\n[Point(2, 5), Point(0, 0)]\n```\n\nIt doesn't work the other way round: a list never uses `__str__` for its items. So when your objects end up in lists, and you want to see them while you test your program, give the class a `__repr__`.\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"class Tag:\n    def __init__(self, text):\n        self.text = text\n\n    def __str__(self):\n        return \"#\" + self.text\n\n    def __repr__(self):\n        return f\"Tag({repr(self.text)})\"\n\n\ntag = Tag(\"python\")\nprint(tag)\nprint(repr(tag))\nprint([tag])\nprint(f\"Tagged {tag}\")\n","solution":"class Tag:\n    def __init__(self, text):\n        self.text = text\n\n    def __str__(self):\n        return \"#\" + self.text\n\n    def __repr__(self):\n        return f\"Tag({repr(self.text)})\"\n\n\ntag = Tag(\"python\")\nprint(tag)\nprint(repr(tag))\nprint([tag])\nprint(f\"Tagged {tag}\")\n","hints":["`print` and f-strings use `__str__`; `repr` and a printed list use `__repr__`.","`repr(self.text)` gives the text with single quotes around it: `'python'`."],"tests":[{"name":"Output","stdin":"","expect":"#python\nTag('python')\n[Tag('python')]\nTagged #python","hidden":false}],"require":[],"forbid":[],"lines":["#python","Tag('python')","[Tag('python')]","Tagged #python"],"more":[{"task":"Give the class `Planet` two text forms. `__str__` returns a sentence for people, such as `Mars has 2 moons`. `__repr__` returns the code that would create the planet, such as `Planet('Mars', 2)`. For example,\n\n```python\nmars = Planet(\"Mars\", 2)\nearth = Planet(\"Earth\", 1)\nprint(mars)\nprint(repr(mars))\nprint([mars, earth])\n```\n\nshould print:\n\n```\nMars has 2 moons\nPlanet('Mars', 2)\n[Planet('Mars', 2), Planet('Earth', 1)]\n```\n","kind":"code","seed":"class Planet:\n    def __init__(self, name, moons):\n        self.name = name\n        self.moons = moons\n\n\nif __name__ == \"__main__\":\n    mars = Planet(\"Mars\", 2)\n    print(mars)\n    print(repr(mars))\n","solution":"class Planet:\n    def __init__(self, name, moons):\n        self.name = name\n        self.moons = moons\n\n    def __str__(self):\n        return f\"{self.name} has {self.moons} moons\"\n\n    def __repr__(self):\n        return f\"Planet({repr(self.name)}, {self.moons})\"\n\n\nif __name__ == \"__main__\":\n    mars = Planet(\"Mars\", 2)\n    print(mars)\n    print(repr(mars))\n","hints":["`__str__` returns the sentence: `f\"{self.name} has {self.moons} moons\"`.","In `__repr__`, use `repr(self.name)` for the name, so that it gets its quotes: `f\"Planet({repr(self.name)}, {self.moons})\"`."],"tests":[{"name":"Test 1","stdin":"","call":"mars = Planet(\"Mars\", 2)\nearth = Planet(\"Earth\", 1)\nprint(mars)\nprint(repr(mars))\nprint([mars, earth])\n","expect":"Mars has 2 moons\nPlanet('Mars', 2)\n[Planet('Mars', 2), Planet('Earth', 1)]","hidden":false},{"name":"Test 2","stdin":"","call":"planets = [Planet(\"Jupiter\", 95), Planet(\"Venus\", 0)]\nprint(planets)\nfor planet in planets:\n    print(planet)\nprint(f\"Biggest: {planets[0]}\")\n","expect":"[Planet('Jupiter', 95), Planet('Venus', 0)]\nJupiter has 95 moons\nVenus has 0 moons\nBiggest: Jupiter has 95 moons","hidden":true}],"require":[{"pattern":"def\\s+__repr__\\s*\\(","message":"Write the method `__repr__`."},{"pattern":"def\\s+__str__\\s*\\(","message":"Write the method `__str__`."}],"forbid":[]},{"task":"Write the class `Position`, a square on a game board, from scratch:\n\n- The constructor takes `x` and `y`.\n- `__repr__` returns text such as `Position(3, 1)`. There's no `__str__`: `__repr__` is enough.\n- `__eq__` says two positions are equal when both `x` and `y` are the same.\n- `moved(dx, dy)` returns a **new** position, `dx` squares further right and `dy` squares further up.\n\nFor example,\n\n```python\nstart = Position(0, 0)\ngoal = Position(3, 1)\nprint(start.moved(2, 1))\nprint(start.moved(3, 1) == goal)\nprint([start, goal])\n```\n\nshould print:\n\n```\nPosition(2, 1)\nTrue\n[Position(0, 0), Position(3, 1)]\n```\n","kind":"code","seed":"# Write the class Position here.\n\n\nif __name__ == \"__main__\":\n    start = Position(0, 0)\n    print(start.moved(2, 1))\n","solution":"class Position:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\n    def __repr__(self):\n        return f\"Position({self.x}, {self.y})\"\n\n    def __eq__(self, other):\n        return self.x == other.x and self.y == other.y\n\n    def moved(self, dx, dy):\n        return Position(self.x + dx, self.y + dy)\n\n\nif __name__ == \"__main__\":\n    start = Position(0, 0)\n    print(start.moved(2, 1))\n","hints":["Start with the constructor and `__repr__`, and print a position to see that it works.","`moved` doesn't change `self`: it returns `Position(self.x + dx, self.y + dy)`.","`__eq__` compares both coordinates and combines the comparisons with `and`."],"tests":[{"name":"Test 1","stdin":"","call":"start = Position(0, 0)\ngoal = Position(3, 1)\nprint(start.moved(2, 1))\nprint(start.moved(3, 1) == goal)\nprint([start, goal])\n","expect":"Position(2, 1)\nTrue\n[Position(0, 0), Position(3, 1)]","hidden":false},{"name":"Position(5, 5).moved(-1, 2)","stdin":"","call":"Position(5, 5).moved(-1, 2)\n","expect":"Position(4, 7)","hidden":false,"returns":true},{"name":"Test 3","stdin":"","call":"here = Position(4, 7)\npath = [here.moved(1, 0), here.moved(0, 1), here]\nprint(path)\nprint(here.moved(-4, -7) == Position(0, 0))\nprint(here == Position(7, 4))\nprint(Position(1, 2) in path)\nprint(here)\n","expect":"[Position(5, 7), Position(4, 8), Position(4, 7)]\nTrue\nFalse\nFalse\nPosition(4, 7)","hidden":true}],"require":[{"pattern":"def\\s+__repr__\\s*\\(","message":"Write the method `__repr__`."},{"pattern":"def\\s+__eq__\\s*\\(","message":"Write the method `__eq__`."}],"forbid":[]}]}