{"id":"classes-objects-in-functions","slug":"objects-and-functions","title":"Your objects in functions and lists","text":"Objects of your own classes are values like any others. Functions can take them as arguments and return them, and lists can hold them.\n\n### Functions that make and change objects\n\nA function can create a new object and return it, and a function can change an object it is given:\n\n```python\nclass Hike:\n    def __init__(self, name, km, climb):\n        self.name = name\n        self.km = km\n        self.climb = climb\n\n\ndef short_version(hike):\n    return Hike(hike.name + \" (short)\", hike.km / 2, hike.climb / 2)\n\n\ndef add_detour(hike, km):\n    hike.km += km\n\n\nridge = Hike(\"Ridge Loop\", 12, 600)\neasy = short_version(ridge)\nadd_detour(ridge, 3)\n\nprint(ridge.name, ridge.km)\nprint(easy.name, easy.km)\n```\n\n```output\nRidge Loop 15\nRidge Loop (short) 6.0\n```\n\n`add_detour` gets a reference to the same object that `ridge` refers to, so the change is still there after the function ends, just as with a list passed to a function. `short_version` makes a new, separate object, so the detour added to `ridge` afterwards doesn't change `easy`.\n\n### Lists of objects\n\nA list of objects works like any list. Loop through it and use each object's attributes:\n\n```python\nclass Hike:\n    def __init__(self, name, km, climb):\n        self.name = name\n        self.km = km\n        self.climb = climb\n\n\ndef hikes_up_to(hikes, max_km):\n    found = []\n    for hike in hikes:\n        if hike.km <= max_km:\n            found.append(hike)\n    return found\n\n\nhikes = [Hike(\"Ridge Loop\", 12, 600), Hike(\"Lake Path\", 5, 40), Hike(\"Old Mill\", 8, 150)]\n\nfor hike in hikes_up_to(hikes, 8):\n    print(f\"{hike.name}: {hike.km} km\")\n\ntotal = 0\nfor hike in hikes:\n    total += hike.climb\nprint(\"Total climb:\", total, \"m\")\n```\n\n```output\nLake Path: 5 km\nOld Mill: 8 km\nTotal climb: 790 m\n```\n\n`hikes_up_to` returns a new list, but the objects in it are the same `Hike` objects as in the original list, not copies.\n\n### Printing an object\n\nWhat about `print(hike)`? For an object of your own class, Python doesn't know what to show, so it prints the class's name and the object's address in memory, something like this:\n\n```text\n<__main__.Hike object at 0x7f2b5c3e1a90>\n```\n\nThe address changes from run to run, so for now, print the attributes you want to see. A later module shows how to teach a class to print itself nicely.\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"class Cup:\n    def __init__(self, ml):\n        self.ml = ml\n\n\ndef fill_up(cup):\n    cup.ml = 250\n\n\ndef new_cup():\n    return Cup(100)\n\n\na = new_cup()\nb = new_cup()\nfill_up(a)\nprint(a.ml, b.ml)\nb.ml -= 30\nprint(a.ml, b.ml)\n","solution":"class Cup:\n    def __init__(self, ml):\n        self.ml = ml\n\n\ndef fill_up(cup):\n    cup.ml = 250\n\n\ndef new_cup():\n    return Cup(100)\n\n\na = new_cup()\nb = new_cup()\nfill_up(a)\nprint(a.ml, b.ml)\nb.ml -= 30\nprint(a.ml, b.ml)\n","hints":["Each call of `new_cup` makes a new, separate cup with 100 ml.","`fill_up(a)` changes the cup that `a` refers to, so it has 250 ml after the call. `b` is another cup.","Taking 30 ml out of `b` changes only that cup."],"tests":[{"name":"Output","stdin":"","expect":"250 100\n250 70","hidden":false}],"require":[],"forbid":[],"lines":["250 100","250 70"],"more":[{"task":"The starter code has a class `Movie`. Write a function `longest(movies)` that takes a list of `Movie` objects and returns the longest one: the `Movie` object itself, not its title. When two movies are equally long, return the one that comes first. The list always has at least one movie.\n\nThis code:\n\n```python\nmovies = [Movie(\"Up\", 2009, 96), Movie(\"Dune\", 2021, 155), Movie(\"Coco\", 2017, 105)]\nbest = longest(movies)\nprint(best.title, best.minutes)\n```\n\nprints\n\n```\nDune 155\n```\n","kind":"code","seed":"class Movie:\n    def __init__(self, title, year, minutes):\n        self.title = title\n        self.year = year\n        self.minutes = minutes\n\n\ndef longest(movies):\n    pass\n\n\nif __name__ == \"__main__\":\n    movies = [Movie(\"Up\", 2009, 96), Movie(\"Dune\", 2021, 155), Movie(\"Coco\", 2017, 105)]\n    best = longest(movies)\n    print(best.title, best.minutes)\n","solution":"class Movie:\n    def __init__(self, title, year, minutes):\n        self.title = title\n        self.year = year\n        self.minutes = minutes\n\n\ndef longest(movies):\n    best = movies[0]\n    for movie in movies:\n        if movie.minutes > best.minutes:\n            best = movie\n    return best\n\n\nif __name__ == \"__main__\":\n    movies = [Movie(\"Up\", 2009, 96), Movie(\"Dune\", 2021, 155), Movie(\"Coco\", 2017, 105)]\n    best = longest(movies)\n    print(best.title, best.minutes)\n","hints":["Keep the longest movie found so far in a variable, starting with the first one in the list.","Compare the attribute: `if movie.minutes > best.minutes:`, and keep the longer movie.","Return the object itself, so that the caller can read any of its attributes."],"tests":[{"name":"Test 1","stdin":"","call":"movies = [Movie(\"Up\", 2009, 96), Movie(\"Dune\", 2021, 155), Movie(\"Coco\", 2017, 105)]\nbest = longest(movies)\nprint(best.title, best.minutes)\n","expect":"Dune 155","hidden":false},{"name":"Test 2","stdin":"","call":"movies = [Movie(\"Heat\", 1995, 170), Movie(\"Jaws\", 1975, 124)]\nbest = longest(movies)\nprint(best.title, best.year)\n","expect":"Heat 1995","hidden":true},{"name":"Test 3","stdin":"","call":"movies = [Movie(\"Alien\", 1979, 117), Movie(\"Brave\", 2012, 93), Movie(\"Clue\", 1985, 117)]\nprint(longest(movies).title)\nprint(longest([Movie(\"Solo\", 2018, 135)]).title)\n","expect":"Alien\nSolo","hidden":true}],"require":[],"forbid":[]},{"task":"Write a function `from_decade(movies, decade)` that takes a list of `Movie` objects and the first year of a decade, such as 1990, and returns a new list with the movies from that decade (from 1990 to 1999), in their original order.\n\nThis code:\n\n```python\nmovies = [Movie(\"Heat\", 1995, 170), Movie(\"Up\", 2009, 96), Movie(\"Fargo\", 1996, 98)]\nfor movie in from_decade(movies, 1990):\n    print(movie.title, movie.year)\n```\n\nprints\n\n```\nHeat 1995\nFargo 1996\n```\n","kind":"code","seed":"class Movie:\n    def __init__(self, title, year, minutes):\n        self.title = title\n        self.year = year\n        self.minutes = minutes\n\n\ndef from_decade(movies, decade):\n    pass\n\n\nif __name__ == \"__main__\":\n    movies = [Movie(\"Heat\", 1995, 170), Movie(\"Up\", 2009, 96), Movie(\"Fargo\", 1996, 98)]\n    for movie in from_decade(movies, 1990):\n        print(movie.title, movie.year)\n","solution":"class Movie:\n    def __init__(self, title, year, minutes):\n        self.title = title\n        self.year = year\n        self.minutes = minutes\n\n\ndef from_decade(movies, decade):\n    found = []\n    for movie in movies:\n        if decade <= movie.year < decade + 10:\n            found.append(movie)\n    return found\n\n\nif __name__ == \"__main__\":\n    movies = [Movie(\"Heat\", 1995, 170), Movie(\"Up\", 2009, 96), Movie(\"Fargo\", 1996, 98)]\n    for movie in from_decade(movies, 1990):\n        print(movie.title, movie.year)\n","hints":["Start with an empty list, go through the movies and append the ones that match.","A movie is from the decade when its year is at least `decade` and less than `decade + 10`.","Return the new list of `Movie` objects, not a list of titles."],"tests":[{"name":"Test 1","stdin":"","call":"movies = [Movie(\"Heat\", 1995, 170), Movie(\"Up\", 2009, 96), Movie(\"Fargo\", 1996, 98)]\nfor movie in from_decade(movies, 1990):\n    print(movie.title, movie.year)\n","expect":"Heat 1995\nFargo 1996","hidden":false},{"name":"Test 2","stdin":"","call":"movies = [Movie(\"Jaws\", 1975, 124), Movie(\"Alien\", 1979, 117), Movie(\"Tron\", 1982, 96), Movie(\"Rocky\", 1976, 120), Movie(\"Big\", 1988, 104)]\nfor movie in from_decade(movies, 1970):\n    print(movie.title)\nprint(len(from_decade(movies, 1980)))\nprint(len(from_decade(movies, 2000)))\n","expect":"Jaws\nAlien\nRocky\n2\n0","hidden":true},{"name":"Test 3","stdin":"","call":"movies = [Movie(\"Edge\", 1990, 100), Movie(\"Ends\", 1999, 90), Movie(\"Next\", 2000, 95)]\nfor movie in from_decade(movies, 1990):\n    print(movie.title)\n","expect":"Edge\nEnds","hidden":true}],"require":[],"forbid":[]}]}