{"id":"object-references-compare","slug":"comparing-objects","title":"Comparing two objects of the same class","text":"Often you want to compare two objects of the same kind: is this parcel heavier than that one? Is this runner faster? You could write a function that takes both objects:\n\n```python\ndef heavier(first, second):\n    return first.weight > second.weight\n```\n\nIn object-oriented programming, though, the code that works on parcels belongs in the `Parcel` class, as a method. A method can take another object of its own class as a parameter. Then one parcel is `self`, the one the method is called on, and the other is the parameter, often called `other`:\n\n```python\nclass Parcel:\n    def __init__(self, label, weight):\n        self.label = label\n        self.weight = weight\n\n    def heavier_than(self, other):\n        return self.weight > other.weight\n\n\nbooks = Parcel(\"books\", 12)\npillow = Parcel(\"pillow\", 2)\n\nif books.heavier_than(pillow):\n    print(f\"The {books.label} parcel is heavier than the {pillow.label} parcel\")\nprint(pillow.heavier_than(books))\n```\n\n```output\nThe books parcel is heavier than the pillow parcel\nFalse\n```\n\nIn `books.heavier_than(pillow)`, the object to the left of the dot (`books`) is `self` inside the method, and the object in the parentheses (`pillow`) is `other`. Swap them and you ask the opposite question.\n\nThe method returns the comparison itself. `self.weight > other.weight` is already `True` or `False`, so there's no need for an `if` that returns `True` in one branch and `False` in the other.\n\nIf you write type hints, the hint for `other` is the class's own name: `def heavier_than(self, other: \"Parcel\"):`. Many programs put it in quotes, because older versions of Python read the hint before the class was finished and didn't know the name yet. Python 3.14, which runs your code on this site, accepts it with or without the quotes.\n\n### Helper variables: self or no self?\n\nSometimes a method needs a few values only while it works. As you have seen with helper methods, those are **local variables**, written without `self.`:\n\n```python\nclass Order:\n    def __init__(self, item, unit_price, quantity):\n        self.item = item\n        self.unit_price = unit_price\n        self.quantity = quantity\n\n    def costs_more_than(self, other):\n        my_total = self.unit_price * self.quantity\n        other_total = other.unit_price * other.quantity\n        return my_total > other_total\n\n\npens = Order(\"pens\", 2, 30)\npaper = Order(\"paper\", 7, 5)\nprint(pens.costs_more_than(paper))\nprint(paper.costs_more_than(pens))\n```\n\n```output\nTrue\nFalse\n```\n\n`my_total` and `other_total` disappear when the method ends, which is what you want. Writing `self.my_total = ...` would work too, but it would quietly add a new attribute to the object that stays there after the method is done. Other methods could then use it by accident, and some objects would have the attribute while others wouldn't, depending on which methods happened to run. Keep the rule simple: an attribute (`self.`) is something the object has to remember; a helper variable is scratch paper for one method.\n","task":"A runner is faster than another runner when their time in seconds is smaller. Fill in the blanks in `faster_than` so the program prints:\n\n```\nFalse\nTrue\n```\n","kind":"fill","seed":"class Runner:\n    def __init__(self, name, seconds):\n        self.name = name\n        self.seconds = seconds\n\n    def faster_than(self, other):\n        return self.seconds [[<]] [[other]].seconds\n\n\nana = Runner(\"Ana\", 62)\nben = Runner(\"Ben\", 58)\nprint(ana.faster_than(ben))\nprint(ben.faster_than(ana))\n","solution":"class Runner:\n    def __init__(self, name, seconds):\n        self.name = name\n        self.seconds = seconds\n\n    def faster_than(self, other):\n        return self.seconds < other.seconds\n\n\nana = Runner(\"Ana\", 62)\nben = Runner(\"Ben\", 58)\nprint(ana.faster_than(ben))\nprint(ben.faster_than(ana))\n","hints":["Inside the method, `self` is the runner before the dot and `other` is the runner in the parentheses.","A smaller time is faster, so compare with `<`: `self.seconds < other.seconds`."],"tests":[{"name":"Output","stdin":"","expect":"False\nTrue","hidden":false}],"require":[],"forbid":[],"more":[{"task":"Write the method `larger(self, other)` in the class `Rectangle`. It compares the areas (width times height) of the two rectangles and returns the one with the larger area: the object itself. When the areas are equal, it returns `self`.\n\nFor example, this code:\n\n```python\ntile = Rectangle(3, 4)\ndoor = Rectangle(2, 7)\nprint(tile.larger(door))\nprint(door.larger(tile))\n```\n\nprints:\n\n```\n2 x 7\n2 x 7\n```\n","kind":"code","seed":"class Rectangle:\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n\n    def larger(self, other):\n        pass\n\n    def __str__(self):\n        return f\"{self.width} x {self.height}\"\n\n\nif __name__ == \"__main__\":\n    tile = Rectangle(3, 4)\n    door = Rectangle(2, 7)\n    print(tile.larger(door))\n    print(door.larger(tile))\n","solution":"class Rectangle:\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n\n    def larger(self, other):\n        my_area = self.width * self.height\n        other_area = other.width * other.height\n        if other_area > my_area:\n            return other\n        return self\n\n    def __str__(self):\n        return f\"{self.width} x {self.height}\"\n\n\nif __name__ == \"__main__\":\n    tile = Rectangle(3, 4)\n    door = Rectangle(2, 7)\n    print(tile.larger(door))\n    print(door.larger(tile))\n","hints":["Work out the two areas first, in two local variables (without `self.`).","The method returns an object, either `self` or `other`, not an area.","Return `other` only when its area is strictly larger; in every other case return `self`."],"tests":[{"name":"Test 1","stdin":"","call":"tile = Rectangle(3, 4)\ndoor = Rectangle(2, 7)\nprint(tile.larger(door))\nprint(door.larger(tile))\n","expect":"2 x 7\n2 x 7","hidden":false},{"name":"Test 2","stdin":"","call":"a = Rectangle(6, 1)\nb = Rectangle(2, 3)\nprint(a.larger(b))\nprint(b.larger(a))\nprint(a.larger(b) is a)\n","expect":"6 x 1\n2 x 3\nTrue","hidden":true},{"name":"Test 3","stdin":"","call":"big = Rectangle(10, 10)\nsmall = Rectangle(1, 2)\nprint(small.larger(big))\nprint(big.larger(small).width)\n","expect":"10 x 10\n10","hidden":true}],"require":[],"forbid":[]},{"task":"A program's version number has three parts, such as `2.10.3`: major, minor and patch. One version is newer than another when its major number is bigger. If the major numbers are the same, the minor numbers decide, and if those are the same too, the patch numbers decide.\n\nWrite the method `newer_than(self, other)` in the class `Version`. It returns `True` when `self` is newer than `other`, and `False` otherwise (also when they are the same version).\n\nFor example, this code:\n\n```python\nold = Version(2, 9, 7)\nnew = Version(2, 10, 0)\nprint(new.newer_than(old))\nprint(old.newer_than(new))\n```\n\nprints:\n\n```\nTrue\nFalse\n```\n","kind":"code","seed":"class Version:\n    def __init__(self, major, minor, patch):\n        self.major = major\n        self.minor = minor\n        self.patch = patch\n\n    def newer_than(self, other):\n        pass\n\n    def __str__(self):\n        return f\"{self.major}.{self.minor}.{self.patch}\"\n\n\nif __name__ == \"__main__\":\n    old = Version(2, 9, 7)\n    new = Version(2, 10, 0)\n    print(new.newer_than(old))\n    print(old.newer_than(new))\n","solution":"class Version:\n    def __init__(self, major, minor, patch):\n        self.major = major\n        self.minor = minor\n        self.patch = patch\n\n    def newer_than(self, other):\n        if self.major != other.major:\n            return self.major > other.major\n        if self.minor != other.minor:\n            return self.minor > other.minor\n        return self.patch > other.patch\n\n    def __str__(self):\n        return f\"{self.major}.{self.minor}.{self.patch}\"\n\n\nif __name__ == \"__main__\":\n    old = Version(2, 9, 7)\n    new = Version(2, 10, 0)\n    print(new.newer_than(old))\n    print(old.newer_than(new))\n","hints":["Compare the major numbers first. If they differ, they alone decide the answer.","`if self.major != other.major: return self.major > other.major`. Do the same for the minor numbers next.","When both the major and the minor numbers are the same, the answer is `self.patch > other.patch`."],"tests":[{"name":"Test 1","stdin":"","call":"old = Version(2, 9, 7)\nnew = Version(2, 10, 0)\nprint(new.newer_than(old))\nprint(old.newer_than(new))\n","expect":"True\nFalse","hidden":false},{"name":"Test 2","stdin":"","call":"a = Version(3, 0, 0)\nb = Version(2, 99, 99)\nprint(a.newer_than(b))\nprint(b.newer_than(a))\n","expect":"True\nFalse","hidden":true},{"name":"Test 3","stdin":"","call":"a = Version(1, 4, 2)\nb = Version(1, 4, 10)\nprint(a.newer_than(b))\nprint(b.newer_than(a))\nprint(a.newer_than(Version(1, 4, 2)))\n","expect":"False\nTrue\nFalse","hidden":true},{"name":"Test 4","stdin":"","call":"print(Version(5, 1, 0).newer_than(Version(4, 8, 9)))\nprint(Version(1, 2, 3).newer_than(Version(1, 1, 9)))\n","expect":"True\nTrue","hidden":true}],"require":[],"forbid":[]}]}