{"id":"objects-classes","slug":"classes-and-objects","title":"Classes are blueprints","text":"Floating point numbers can be slightly off, as you have seen: `0.1 + 0.2` isn't quite `0.3`. When a program needs exact answers, for a recipe scaled to three people or a share of a bill, Python's `fractions` module can help. It has a kind of object called `Fraction`, a number that is kept as a numerator and a denominator and is never off:\n\n```python\nfrom fractions import Fraction\n\nprint(0.1 + 0.2)\nprint(Fraction(1, 10) + Fraction(2, 10))\n\nshare = Fraction(3, 4)\nprint(share)\nprint(share + Fraction(1, 6))\nprint(Fraction(4, 10))\n```\n\n```output\n0.30000000000000004\n3/10\n3/4\n11/12\n2/5\n```\n\n`Fraction(3, 4)` creates a new `Fraction` object with the numerator 3 and the denominator 4, and it reduces the fraction to its simplest form on the way, so `Fraction(4, 10)` is `2/5`. Fractions can be added, multiplied and compared like numbers.\n\n### Where objects come from\n\nYou made objects this way before. In part 7, `datetime(2026, 7, 20)` gave you a new `datetime` object and `timedelta(days=3)` a new time difference. A call like these, the name of a kind of object followed by the values the new object needs in parentheses, is a **constructor** call: it builds a new object and gives it to you.\n\nSome objects need no constructor call, because Python lets you write them straight into your code: numbers such as `42` and `3.5`, `True`, `False` and `None`, strings, lists, dictionaries and tuples. Most other kinds of objects, such as fractions and dates, have no such shorthand, and a constructor call is how you get one.\n\n### One class, many objects\n\nThe kind of an object is its class, as `type` showed in the first step. A **class** describes one kind of object: what data each object of that kind holds and which methods it has. Think of the blueprint of a house: from one blueprint you can build as many houses as you like, and each house has its own address and its own paint. An object made from a class is called an **instance** of the class, and the variables that each object has are its **attributes**.\n\nEvery `Fraction` object has its own values for the attributes `numerator` and `denominator`. You read an attribute with the object, a dot and the attribute's name:\n\n```python\nfrom fractions import Fraction\n\nsmall = Fraction(3, 12)\nbig = Fraction(7, 2)\n\nprint(small.numerator, small.denominator)\nprint(big.numerator, big.denominator)\n```\n\n```output\n1 4\n7 2\n```\n\n(`3/12` was reduced to `1/4` when the object was made.)\n\nYou have read attributes before: `meeting.year` and `meeting.hour` in part 7 were attributes of a `datetime` object. The `datetime` module also has a smaller class, `date`, for a day without a time of day. Its objects have just the attributes `year`, `month` and `day`:\n\n```pycon\n>>> from datetime import date\n>>> launch = date(2026, 7, 20)\n>>> print(launch)\n2026-07-20\n>>> launch.year\n2026\n>>> launch.month\n7\n>>> type(launch)\n<class 'datetime.date'>\n>>> from fractions import Fraction\n>>> type(Fraction(1, 2))\n<class 'fractions.Fraction'>\n```\n\n`type` names the module a class comes from, then the class. Notice that `Fraction` starts with a capital letter and `date` doesn't. A capital letter is the custom for the classes you write yourself (PEP 8 asks for it, as the next module shows), and many library classes, such as `Fraction`, follow it. It's a habit, though, not a rule: Python's own classes `int`, `str` and `list` have small letters, and so do some library classes, such as `date` and `datetime`.\n\nWriting programs out of classes and the objects made from them is called **object-oriented programming**. In the next module you'll write classes of your own. First, a closer look at the objects of classes that Python already has.\n","task":"Fill in the blanks, so that the program imports the class, then prints the fraction and its denominator:\n\n```\n1/4\n4\n```\n","kind":"fill","seed":"from fractions import [[Fraction]]\n\nslice_size = Fraction(2, 8)\nprint(slice_size)\nprint(slice_size.[[denominator]])\n","solution":"from fractions import Fraction\n\nslice_size = Fraction(2, 8)\nprint(slice_size)\nprint(slice_size.denominator)\n","hints":["The class you import has the same name as the one the program calls on the next line.","The bottom part of a fraction is its denominator, and the attribute has that name: `slice_size.denominator`, with no parentheses."],"tests":[{"name":"Output","stdin":"","expect":"1/4\n4","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"from fractions import Fraction\n\na = Fraction(1, 4)\nb = Fraction(2, 4)\nprint(b)\nprint(a + b)\nprint((a + b).numerator)\nprint(a * 2)\n","solution":"from fractions import Fraction\n\na = Fraction(1, 4)\nb = Fraction(2, 4)\nprint(b)\nprint(a + b)\nprint((a + b).numerator)\nprint(a * 2)\n","hints":["A new `Fraction` is reduced to its simplest form, so `Fraction(2, 4)` prints as `1/2`.","1/4 + 2/4 is 3/4, and the numerator is the top part of that new fraction.","Twice one quarter is two quarters, and that is reduced too."],"tests":[{"name":"Output","stdin":"","expect":"1/2\n3/4\n3\n1/2","hidden":false}],"require":[],"forbid":[],"lines":["1/2","3/4","3","1/2"]},{"task":"A fraction like 7/4 is easier to read as a mixed number: `1 3/4`. Write a function `as_mixed(fraction)` that takes a `Fraction` object and returns it as a string in this form: the whole part, a space, then the rest as a fraction. When there's no whole part, return only the fraction (`3/4`), and when there's no rest, only the whole number (`2`). The fractions are always positive.\n\nThis code:\n\n```python\nprint(as_mixed(Fraction(7, 4)))\nprint(as_mixed(Fraction(3, 4)))\nprint(as_mixed(Fraction(8, 4)))\n```\n\nprints\n\n```\n1 3/4\n3/4\n2\n```\n","kind":"code","seed":"from fractions import Fraction\n\n\ndef as_mixed(fraction):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(as_mixed(Fraction(7, 4)))\n","solution":"from fractions import Fraction\n\n\ndef as_mixed(fraction):\n    whole = fraction.numerator // fraction.denominator\n    rest = fraction.numerator % fraction.denominator\n    if rest == 0:\n        return str(whole)\n    if whole == 0:\n        return f\"{rest}/{fraction.denominator}\"\n    return f\"{whole} {rest}/{fraction.denominator}\"\n\n\nif __name__ == \"__main__\":\n    print(as_mixed(Fraction(7, 4)))\n","hints":["Use the attributes `fraction.numerator` and `fraction.denominator`. For 7/4, the whole part is 7 // 4 and the rest is 7 % 4.","Build the string with an f-string: `f\"{whole} {rest}/{fraction.denominator}\"`.","When the rest is 0, return only the whole part as a string; when the whole part is 0, return only the fraction."],"tests":[{"name":"Test 1","stdin":"","call":"print(as_mixed(Fraction(7, 4)))\nprint(as_mixed(Fraction(3, 4)))\nprint(as_mixed(Fraction(8, 4)))\n","expect":"1 3/4\n3/4\n2","hidden":false},{"name":"Test 2","stdin":"","call":"print(as_mixed(Fraction(22, 7)))\nprint(as_mixed(Fraction(1, 9)))\nprint(as_mixed(Fraction(15, 5)))\n","expect":"3 1/7\n1/9\n3","hidden":true},{"name":"as_mixed(Fraction(10, 4))","stdin":"","call":"as_mixed(Fraction(10, 4))\n","expect":"'2 1/2'","hidden":true,"returns":true}],"require":[],"forbid":[]}]}