{"id":"objects-attributes","slug":"methods-and-attributes","title":"Methods and attributes","text":"After an object's dot there can be two kinds of things: attributes and methods. A `date` has the attribute `year`, and it has the method `weekday`, which you used with `datetime` objects in part 7: it gives the day of the week as a number from 0 (Monday) to 6 (Sunday).\n\n```python\nfrom datetime import date\n\nstart = date(2026, 10, 17)\n\nprint(start.year)\nprint(start.weekday())\n```\n\n```output\n2026\n5\n```\n\n- `start.year` is an **attribute**: a value stored in the object. There are no parentheses after it.\n- `start.weekday()` is a **method call**: the method works something out from the object's data. The parentheses are what call it, even when there are no arguments.\n\nHow do you know which is which? The documentation of a class lists its attributes and its methods. For `date`, `year`, `month` and `day` are attributes, and `weekday()` is a method. For `Fraction`, `numerator` and `denominator` are attributes.\n\n### A missing pair of parentheses\n\nMixing the two up is a common mistake, and one way round it doesn't even stop the program. This program is meant to spot Saturdays, and October 17, 2026 is one:\n\n```python\nfrom datetime import date\n\nday = date(2026, 10, 17)\nif day.weekday == 5:\n    print(\"Saturday!\")\nelse:\n    print(\"Not a Saturday\")\n```\n\n```output\nNot a Saturday\n```\n\nWithout parentheses, `day.weekday` isn't the 5 that the method would work out: it is the method itself. A method is never equal to a number, so the condition is always `False`, and Python finds nothing wrong to report. Write `day.weekday() == 5`.\n\nThe other way round, parentheses after an attribute, does stop the program. A fraction's denominator is a number stored in the object, and Python can't call a number:\n\n```python\nfrom fractions import Fraction\n\nhalf = Fraction(1, 2)\nprint(half.denominator())\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 4, in <module>\n    print(half.denominator())\n          ~~~~~~~~~~~~~~~~^^\nTypeError: 'int' object is not callable\n```\n\n\"Not callable\" means there are parentheses after something that isn't a function or a method: here the `int` 2.\n\n### Functions that take objects\n\nObjects are values like any others, so your own functions can take them as arguments and return them. This program has a function that names the day of the week of a date, and one that picks the dates of one month out of a list:\n\n```python\nfrom datetime import date\n\n\ndef day_name(day):\n    names = [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"]\n    return names[day.weekday()]\n\n\ndef in_month(days, month):\n    found = []\n    for day in days:\n        if day.month == month:\n            found.append(day)\n    return found\n\n\nmeetings = [date(2026, 5, 11), date(2026, 6, 3), date(2026, 5, 29)]\n\nfor day in in_month(meetings, 5):\n    print(day, day_name(day))\n```\n\n```output\n2026-05-11 Monday\n2026-05-29 Friday\n```\n\n`day_name` uses the method's result as an index into a list. `in_month` reads an attribute of each object and returns a new list with some of the same `date` objects in it.\n","task":"This program mixes up attributes and methods. Fix it, so that it prints:\n\n```\nMonth: 3\nWeekday: 6\nYear: 2026\n```\n","kind":"code","seed":"from datetime import date\n\nholiday = date(2026, 3, 1)\nprint(\"Month:\", holiday.month())\nprint(\"Weekday:\", holiday.weekday)\nprint(\"Year:\", holiday.year)\n","solution":"from datetime import date\n\nholiday = date(2026, 3, 1)\nprint(\"Month:\", holiday.month)\nprint(\"Weekday:\", holiday.weekday())\nprint(\"Year:\", holiday.year)\n","hints":["Check the program and read the error. Which name has parentheses after it that it shouldn't have?","Once that runs, look at the weekday line. Something like `<built-in method weekday of datetime.date object at 0x...>` is what Python prints for a method that was never called: the method itself, not its result.","`month` is an attribute, so it has no parentheses. `weekday` is a method, so it needs them: `holiday.weekday()`."],"tests":[{"name":"Output","stdin":"","expect":"Month: 3\nWeekday: 6\nYear: 2026","hidden":false}],"require":[],"forbid":[],"more":[{"task":"Write a function `day_label(day)` that takes a `date` object and returns a short label for it: the first three letters of the day of the week, a space, then the day, the month and the year separated by dots.\n\nThis code:\n\n```python\nprint(day_label(date(2026, 10, 17)))\nprint(day_label(date(2026, 3, 2)))\n```\n\nprints\n\n```\nSat 17.10.2026\nMon 2.3.2026\n```\n","kind":"code","seed":"from datetime import date\n\n\ndef day_label(day):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(day_label(date(2026, 10, 17)))\n","solution":"from datetime import date\n\n\ndef day_label(day):\n    names = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\n    return f\"{names[day.weekday()]} {day.day}.{day.month}.{day.year}\"\n\n\nif __name__ == \"__main__\":\n    print(day_label(date(2026, 10, 17)))\n","hints":["Make a list of the short day names, starting from Monday, and pick one with `day.weekday()` as the index.","The day, the month and the year are attributes: `day.day`, `day.month` and `day.year`, with no parentheses.","Put it together with an f-string: `f\"{...} {day.day}.{day.month}.{day.year}\"`."],"tests":[{"name":"Test 1","stdin":"","call":"print(day_label(date(2026, 10, 17)))\nprint(day_label(date(2026, 3, 2)))\n","expect":"Sat 17.10.2026\nMon 2.3.2026","hidden":false},{"name":"Test 2","stdin":"","call":"print(day_label(date(2027, 1, 1)))\nprint(day_label(date(2026, 12, 31)))\n","expect":"Fri 1.1.2027\nThu 31.12.2026","hidden":true},{"name":"day_label(date(2026, 6, 3))","stdin":"","call":"day_label(date(2026, 6, 3))\n","expect":"'Wed 3.6.2026'","hidden":true,"returns":true}],"require":[],"forbid":[]},{"task":"Write a function `weekend_count(days)` that takes a list of `date` objects and returns how many of them fall on a Saturday or a Sunday.\n\nThis code:\n\n```python\ndays = [date(2026, 10, 17), date(2026, 10, 18), date(2026, 10, 19)]\nprint(weekend_count(days))\n```\n\nprints\n\n```\n2\n```\n","kind":"code","seed":"from datetime import date\n\n\ndef weekend_count(days):\n    pass\n\n\nif __name__ == \"__main__\":\n    days = [date(2026, 10, 17), date(2026, 10, 18), date(2026, 10, 19)]\n    print(weekend_count(days))\n","solution":"from datetime import date\n\n\ndef weekend_count(days):\n    count = 0\n    for day in days:\n        if day.weekday() >= 5:\n            count += 1\n    return count\n\n\nif __name__ == \"__main__\":\n    days = [date(2026, 10, 17), date(2026, 10, 18), date(2026, 10, 19)]\n    print(weekend_count(days))\n","hints":["`weekday()` gives 5 for a Saturday and 6 for a Sunday.","Count with a loop: start from 0, go through the list with `for day in days:` and add one when `day.weekday() >= 5`.","Return the count; don't print it."],"tests":[{"name":"Test 1","stdin":"","call":"days = [date(2026, 10, 17), date(2026, 10, 18), date(2026, 10, 19)]\nprint(weekend_count(days))\n","expect":"2","hidden":false},{"name":"weekend_count([date(2026, 5, 4), date(2026, 5, 5), date(2026, 5, 6)])","stdin":"","call":"weekend_count([date(2026, 5, 4), date(2026, 5, 5), date(2026, 5, 6)])\n","expect":"0","hidden":true,"returns":true},{"name":"weekend_count([date(2026, 3, 1), date(2026, 3, 8), date(2026, 3, 14), date(2026, 3, 20)])","stdin":"","call":"weekend_count([date(2026, 3, 1), date(2026, 3, 8), date(2026, 3, 14), date(2026, 3, 20)])\n","expect":"3","hidden":true,"returns":true},{"name":"weekend_count([])","stdin":"","call":"weekend_count([])\n","expect":"0","hidden":true,"returns":true}],"require":[],"forbid":[]}]}