{"id":"class-attributes-methods","slug":"class-methods","title":"Class methods and static methods","text":"Methods can belong to the class too. You call such a method through the class's name, without creating an object first.\n\nA **class method** has the decorator `@classmethod` above it, and its first parameter is **`cls`** instead of `self`. Where `self` is the object, `cls` is the class itself, and Python fills it in for you just as it fills in `self`. A **static method** has the decorator `@staticmethod` and no such parameter at all: it's an ordinary function that lives in the class because it belongs with it.\n\n```python\nclass Time:\n    def __init__(self, hours, minutes):\n        if not Time.valid(hours, minutes):\n            raise ValueError(f\"Not a valid time: {hours}:{minutes}\")\n        self.hours = hours\n        self.minutes = minutes\n\n    @staticmethod\n    def valid(hours, minutes):\n        return 0 <= hours < 24 and 0 <= minutes < 60\n\n    @classmethod\n    def from_text(cls, text):\n        parts = text.split(\":\")\n        return cls(int(parts[0]), int(parts[1]))\n\n    @classmethod\n    def midnight(cls):\n        return cls(0, 0)\n\n    def __str__(self):\n        return f\"{self.hours:02}:{self.minutes:02}\"\n\n\nprint(Time.valid(25, 0))\nlunch = Time.from_text(\"12:30\")\nprint(lunch)\nprint(Time.midnight())\n```\n\n```output\nFalse\n12:30\n00:00\n```\n\n(In the f-string, `:02` writes a number with at least two digits, padding it with a zero.)\n\n- `Time.valid(25, 0)` looks only at the two numbers it gets, so it needs no `Time` object to work on. Any code can ask it whether a time makes sense, and `__init__` asks it first thing, before it stores anything.\n- `from_text` and `midnight` create new objects: `cls(...)` calls the class, exactly as `Time(...)` would. Class methods like these are a handy way to offer more than one way to create an object.\n\n### Which one to choose\n\nBoth kinds are called the same way, `Time.valid(...)` or `Time.from_text(...)`. The difference is what they get:\n\n- an ordinary method gets the object (`self`), and is called on an object;\n- a class method gets the class (`cls`), so it can create objects with `cls(...)` and use class variables as `cls.name`;\n- a static method gets neither: it works with the arguments you give it. Like any other code, it can still reach the class by its name, for example to read a class variable such as `Ticket.sold` below, but it has no `self` and no `cls`.\n\nA class method that works with a class variable:\n\n```python\nclass Ticket:\n    sold = 0\n\n    def __init__(self):\n        Ticket.sold += 1\n\n    @classmethod\n    def sales_report(cls):\n        return f\"{cls.sold} tickets sold\"\n\n\nTicket()\nTicket()\nprint(Ticket.sales_report())\n```\n\n```output\n2 tickets sold\n```\n\nCalling an ordinary method through the class doesn't work, because there is no object to be its `self`. `Time.__str__()` would stop with `TypeError: Time.__str__() missing 1 required positional argument: 'self'`.\n","task":"Fill in the blanks so that `Rectangle.square(side)` is a class method that creates a square, and the program prints:\n\n```\n3 x 3\n9\n```\n","kind":"fill","seed":"class Rectangle:\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n\n    @[[classmethod]]\n    def square(cls, side):\n        return [[cls]](side, side)\n\n    def area(self):\n        return self.width * self.height\n\n    def __str__(self):\n        return f\"{self.width} x {self.height}\"\n\n\ntile = Rectangle.square(3)\nprint(tile)\nprint(tile.area())\n","solution":"class Rectangle:\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n\n    @classmethod\n    def square(cls, side):\n        return cls(side, side)\n\n    def area(self):\n        return self.width * self.height\n\n    def __str__(self):\n        return f\"{self.width} x {self.height}\"\n\n\ntile = Rectangle.square(3)\nprint(tile)\nprint(tile.area())\n","hints":["The method is called through the class, `Rectangle.square(3)`, and its first parameter is `cls`. Which decorator does it need?","The decorator is `@classmethod`, and `cls(side, side)` creates the new object, just like `Rectangle(side, side)`."],"tests":[{"name":"Output","stdin":"","expect":"3 x 3\n9","hidden":false}],"require":[],"forbid":[],"more":[{"task":"A music player shows a song's length as minutes and seconds, such as `3:05`, with the seconds always written with two digits. Write the class `Song`:\n\n- a static method `format_length(seconds)` that returns a length given in seconds as such a string, for example `\"3:05\"` for 185 seconds;\n- a constructor that takes a title and a length in seconds, and stores them in the attributes `title` and `seconds`;\n- `__str__`, which returns a string such as `Slow River (5:02)`. Let it use the static method.\n\nFor example, this code:\n\n```python\nprint(Song.format_length(185))\nprint(Song.format_length(59))\nprint(Song(\"Slow River\", 302))\n```\n\nprints:\n\n```\n3:05\n0:59\nSlow River (5:02)\n```\n","kind":"code","seed":"class Song:\n    pass\n\n\nif __name__ == \"__main__\":\n    print(Song.format_length(185))\n    print(Song.format_length(59))\n    print(Song(\"Slow River\", 302))\n","solution":"class Song:\n    def __init__(self, title, seconds):\n        self.title = title\n        self.seconds = seconds\n\n    @staticmethod\n    def format_length(seconds):\n        return f\"{seconds // 60}:{seconds % 60:02}\"\n\n    def __str__(self):\n        return f\"{self.title} ({Song.format_length(self.seconds)})\"\n\n\nif __name__ == \"__main__\":\n    print(Song.format_length(185))\n    print(Song.format_length(59))\n    print(Song(\"Slow River\", 302))\n","hints":["`//` and `%` split the seconds: `seconds // 60` is the whole minutes, and `seconds % 60` the seconds left over.","Write the seconds with `:02` in the f-string, as the `Time` example does, so that 5 seconds shows as `05`.","`__str__` calls the static method through the class's name: `Song.format_length(self.seconds)`."],"tests":[{"name":"Test 1","stdin":"","call":"print(Song.format_length(185))\nprint(Song.format_length(59))\nprint(Song(\"Slow River\", 302))\n","expect":"3:05\n0:59\nSlow River (5:02)","hidden":false},{"name":"Test 2","stdin":"","call":"for seconds in [0, 60, 61, 600, 3599]:\n    print(Song.format_length(seconds))\n","expect":"0:00\n1:00\n1:01\n10:00\n59:59","hidden":true},{"name":"Test 3","stdin":"","call":"song = Song(\"Neon Rain\", 187)\nprint(song.title, song.seconds)\nprint(song)\n","expect":"Neon Rain 187\nNeon Rain (3:07)","hidden":true}],"require":[{"pattern":"@staticmethod","raw":true,"message":"Make `format_length` a static method, with `@staticmethod` above it."}],"forbid":[]},{"task":"Write the class `Order`. Each new order gets the next number from the class variable `next_number`, which starts at 1: the first order is number 1, the next number 2, and so on. The class has:\n\n- a constructor that takes the customer's name, stores it in `customer`, and gives the order its `number`;\n- a class method `start_numbering(cls, number)` that sets the number the next order will get;\n- `__str__`, which returns a string such as `Order 1 for Ada`.\n\nFor example, this code:\n\n```python\nprint(Order(\"Ada\"))\nprint(Order(\"Ben\"))\nOrder.start_numbering(100)\nprint(Order(\"Cy\"))\nprint(Order.next_number)\n```\n\nprints:\n\n```\nOrder 1 for Ada\nOrder 2 for Ben\nOrder 100 for Cy\n101\n```\n","kind":"code","seed":"class Order:\n    next_number = 1\n\n\nif __name__ == \"__main__\":\n    print(Order(\"Ada\"))\n    print(Order(\"Ben\"))\n    Order.start_numbering(100)\n    print(Order(\"Cy\"))\n    print(Order.next_number)\n","solution":"class Order:\n    next_number = 1\n\n    def __init__(self, customer):\n        self.customer = customer\n        self.number = Order.next_number\n        Order.next_number += 1\n\n    @classmethod\n    def start_numbering(cls, number):\n        cls.next_number = number\n\n    def __str__(self):\n        return f\"Order {self.number} for {self.customer}\"\n\n\nif __name__ == \"__main__\":\n    print(Order(\"Ada\"))\n    print(Order(\"Ben\"))\n    Order.start_numbering(100)\n    print(Order(\"Cy\"))\n    print(Order.next_number)\n","hints":["In `__init__`, first give the order the current `Order.next_number`, then add one to the class variable.","The class method changes the class variable through `cls`: `cls.next_number = number`.","Don't write `self.next_number += 1`: that would give the order an attribute of its own and leave the shared counter as it was."],"tests":[{"name":"Test 1","stdin":"","call":"print(Order(\"Ada\"))\nprint(Order(\"Ben\"))\nOrder.start_numbering(100)\nprint(Order(\"Cy\"))\nprint(Order.next_number)\n","expect":"Order 1 for Ada\nOrder 2 for Ben\nOrder 100 for Cy\n101","hidden":false},{"name":"Test 2","stdin":"","call":"Order.start_numbering(7)\nfirst = Order(\"Kim\")\nsecond = Order(\"Lou\")\nprint(first.number, second.number)\nprint(first)\nOrder.start_numbering(1)\nprint(Order(\"Max\"))\nprint(second)\n","expect":"7 8\nOrder 7 for Kim\nOrder 1 for Max\nOrder 8 for Lou","hidden":true}],"require":[{"pattern":"@classmethod","raw":true,"message":"Make `start_numbering` a class method, with `@classmethod` above it."}],"forbid":[]}]}