{"id":"objects-everything","slug":"everything-is-an-object","title":"Everything is an object","text":"This module starts the second half of the course, which the MOOC calls the Advanced Course in Programming. Its big idea is **object-oriented programming**: building programs out of objects that keep data together with the operations on that data. You have been using objects all along without calling them that.\n\n### Data that belongs together\n\nWhen several values describe one thing, you have kept them together in a dictionary (or a tuple): a pet's name, species and age, for example. Here are two pets:\n\n```python\npet1 = {\"name\": \"Biscuit\", \"species\": \"cat\", \"age\": 4}\npet2 = {\"name\": \"Pepper\", \"species\": \"dog\", \"age\": 2}\n\npet1[\"age\"] += 1\n\nprint(pet1[\"name\"], pet1[\"age\"])\nprint(pet2[\"name\"], pet2[\"age\"])\n```\n\n```output\nBiscuit 5\nPepper 2\n```\n\nEach dictionary is an **object**: a whole of its own in the computer's memory, holding data that belongs together. The two dictionaries have the same keys, but they are separate objects, so changing one doesn't change the other. This is what programmers mean when they say that objects are **independent**.\n\n### Every value is an object\n\nIn Python, every value is an object: numbers, strings, lists, dictionaries, even `True` and `None`. A variable doesn't hold the object itself but a **reference** to it, as you saw in the References module, where two variables referred to the same list.\n\nEvery object has a type, and the function `type` tells you which:\n\n```pycon\n>>> type(42)\n<class 'int'>\n>>> type(\"Biscuit\")\n<class 'str'>\n>>> type([4, 2])\n<class 'list'>\n>>> type({\"name\": \"Pepper\"})\n<class 'dict'>\n```\n\nPython calls the type of an object its **class**. Classes are what this part of the course is about, and soon you'll write your own.\n\nYou met two more words in the References module. Lists and dictionaries are **mutable**: they can be changed after they are made. Numbers, strings and tuples are **immutable**: they can't. When a program seems to change a string, Python really makes a new string, and the variable then refers to the new one:\n\n```python\nword = \"cat\"\nother = word\nword = word + \"s\"\nprint(word)\nprint(other)\n```\n\n```output\ncats\ncat\n```\n\nThe string `\"cat\"` stays as it was, and `other` still refers to it.\n\n### Objects in and out of functions\n\nA function can take objects as its arguments and return one of them: the whole object, with all its data.\n\n```python\ndef older(pet1, pet2):\n    if pet1[\"age\"] >= pet2[\"age\"]:\n        return pet1\n    return pet2\n\n\ncat = {\"name\": \"Biscuit\", \"species\": \"cat\", \"age\": 4}\ndog = {\"name\": \"Pepper\", \"species\": \"dog\", \"age\": 2}\n\nwinner = older(cat, dog)\nprint(winner[\"name\"])\nprint(winner)\n```\n\n```output\nBiscuit\n{'name': 'Biscuit', 'species': 'cat', 'age': 4}\n```\n\nThe caller gets the whole dictionary back and can use any of its data, not just the name.\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"car1 = {\"model\": \"Zip\", \"km\": 1200}\ncar2 = {\"model\": \"Zip\", \"km\": 1200}\nprint(car1 == car2)\ncar1[\"km\"] += 300\nprint(car1[\"km\"])\nprint(car2[\"km\"])\nprint(car1 == car2)\n","solution":"car1 = {\"model\": \"Zip\", \"km\": 1200}\ncar2 = {\"model\": \"Zip\", \"km\": 1200}\nprint(car1 == car2)\ncar1[\"km\"] += 300\nprint(car1[\"km\"])\nprint(car2[\"km\"])\nprint(car1 == car2)\n","hints":["`==` compares what two dictionaries hold: the same keys with the same values make them equal.","The two dictionaries are separate objects, so adding to `car1[\"km\"]` leaves `car2` as it was.","After the change, the two hold different values for `\"km\"`, so they are no longer equal."],"tests":[{"name":"Output","stdin":"","expect":"True\n1500\n1200\nFalse","hidden":false}],"require":[],"forbid":[],"lines":["True","1500","1200","False"],"more":[{"task":"Write a function `oldest(pets)` that takes a list of pet dictionaries, each with the keys `\"name\"`, `\"species\"` and `\"age\"`, and returns the whole dictionary of the oldest pet. When two pets are equally old, return the one that comes first in the list. The list always has at least one pet.\n\nFor example, with the pets in the starter code, `oldest(pets)` returns `{'name': 'Rex', 'species': 'dog', 'age': 9}`.\n","kind":"code","seed":"def oldest(pets):\n    pass\n\n\nif __name__ == \"__main__\":\n    pets = [\n        {\"name\": \"Biscuit\", \"species\": \"cat\", \"age\": 4},\n        {\"name\": \"Rex\", \"species\": \"dog\", \"age\": 9},\n        {\"name\": \"Nibbles\", \"species\": \"hamster\", \"age\": 1},\n    ]\n    print(oldest(pets))\n","solution":"def oldest(pets):\n    best = pets[0]\n    for pet in pets:\n        if pet[\"age\"] > best[\"age\"]:\n            best = pet\n    return best\n\n\nif __name__ == \"__main__\":\n    pets = [\n        {\"name\": \"Biscuit\", \"species\": \"cat\", \"age\": 4},\n        {\"name\": \"Rex\", \"species\": \"dog\", \"age\": 9},\n        {\"name\": \"Nibbles\", \"species\": \"hamster\", \"age\": 1},\n    ]\n    print(oldest(pets))\n","hints":["Keep the oldest pet found so far in a variable, starting with the first pet in the list.","Go through the list with `for pet in pets:` and replace the oldest pet so far when `pet[\"age\"]` is greater than its age. Greater, not greater or equal, so that the first of two equally old pets wins.","Return the dictionary itself (`return best`), not just its name."],"tests":[{"name":"Test 1","stdin":"","call":"pets = [\n    {\"name\": \"Biscuit\", \"species\": \"cat\", \"age\": 4},\n    {\"name\": \"Rex\", \"species\": \"dog\", \"age\": 9},\n    {\"name\": \"Nibbles\", \"species\": \"hamster\", \"age\": 1},\n]\noldest(pets)\n","expect":"{'name': 'Rex', 'species': 'dog', 'age': 9}","hidden":false,"returns":true},{"name":"Test 2","stdin":"","call":"pets = [\n    {\"name\": \"Kiwi\", \"species\": \"parrot\", \"age\": 12},\n    {\"name\": \"Mo\", \"species\": \"cat\", \"age\": 3},\n]\noldest(pets)\n","expect":"{'name': 'Kiwi', 'species': 'parrot', 'age': 12}","hidden":true,"returns":true},{"name":"Test 3","stdin":"","call":"pets = [\n    {\"name\": \"Ada\", \"species\": \"rabbit\", \"age\": 5},\n    {\"name\": \"Bo\", \"species\": \"rabbit\", \"age\": 5},\n    {\"name\": \"Cy\", \"species\": \"rabbit\", \"age\": 2},\n]\noldest(pets)\n","expect":"{'name': 'Ada', 'species': 'rabbit', 'age': 5}","hidden":true,"returns":true},{"name":"oldest([{\"name\": \"Solo\", \"species\": \"fish\", \"age\": 1}])","stdin":"","call":"oldest([{\"name\": \"Solo\", \"species\": \"fish\", \"age\": 1}])\n","expect":"{'name': 'Solo', 'species': 'fish', 'age': 1}","hidden":true,"returns":true}],"require":[],"forbid":[]},{"task":"Write two functions:\n\n- `new_pet(name, species)` creates and returns a new pet dictionary with the keys `\"name\"`, `\"species\"` and `\"age\"`. A new pet's age is 0.\n- `birthday(pet)` adds one to the age of the pet it is given. It returns nothing: it changes the dictionary itself.\n\nThis code:\n\n```python\ncat = new_pet(\"Biscuit\", \"cat\")\ndog = new_pet(\"Rex\", \"dog\")\nbirthday(cat)\nbirthday(cat)\nprint(cat)\nprint(dog)\n```\n\nprints\n\n```\n{'name': 'Biscuit', 'species': 'cat', 'age': 2}\n{'name': 'Rex', 'species': 'dog', 'age': 0}\n```\n","kind":"code","seed":"def new_pet(name, species):\n    pass\n\n\ndef birthday(pet):\n    pass\n\n\nif __name__ == \"__main__\":\n    cat = new_pet(\"Biscuit\", \"cat\")\n    birthday(cat)\n    print(cat)\n","solution":"def new_pet(name, species):\n    return {\"name\": name, \"species\": species, \"age\": 0}\n\n\ndef birthday(pet):\n    pet[\"age\"] += 1\n\n\nif __name__ == \"__main__\":\n    cat = new_pet(\"Biscuit\", \"cat\")\n    birthday(cat)\n    print(cat)\n","hints":["`new_pet` builds a dictionary from its parameters and returns it: `return {\"name\": name, ...}`, with the age 0.","`birthday` changes the dictionary it gets, so it needs no `return`: `pet[\"age\"] += 1`.","Each call of `new_pet` makes a new dictionary, a separate object, so a birthday of one pet doesn't change the other."],"tests":[{"name":"Test 1","stdin":"","call":"cat = new_pet(\"Biscuit\", \"cat\")\ndog = new_pet(\"Rex\", \"dog\")\nbirthday(cat)\nbirthday(cat)\nprint(cat)\nprint(dog)\n","expect":"{'name': 'Biscuit', 'species': 'cat', 'age': 2}\n{'name': 'Rex', 'species': 'dog', 'age': 0}","hidden":false},{"name":"Test 2","stdin":"","call":"fish = new_pet(\"Bubbles\", \"goldfish\")\nprint(fish)\nfor i in range(5):\n    birthday(fish)\nprint(fish[\"age\"])\n","expect":"{'name': 'Bubbles', 'species': 'goldfish', 'age': 0}\n5","hidden":true},{"name":"Test 3","stdin":"","call":"a = new_pet(\"Twin\", \"cat\")\nb = new_pet(\"Twin\", \"cat\")\nbirthday(b)\nprint(a[\"age\"], b[\"age\"])\n","expect":"0 1","hidden":true}],"require":[],"forbid":[]}]}