{"id":"object-references-same-object","slug":"same-object","title":"Two names for one object","text":"In the References module you saw that a variable doesn't hold a list itself, only a **reference** to it: the information about where the list is in the computer's memory. The same goes for every object, your own included. A variable, a list item or a parameter refers to an object, and the object itself lives elsewhere.\n\nThis matters as soon as you assign one variable to another. `b = a` doesn't copy the object. It copies the reference, so both names refer to the same object:\n\n```python\nclass Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n    def __str__(self):\n        return f\"{self.name}, {self.height} cm\"\n\n\nbasil = Plant(\"Basil\")\nwindowsill = basil\nwindowsill.height += 5\n\nprint(basil)\nprint(windowsill)\n```\n\n```output\nBasil, 15 cm\nBasil, 15 cm\n```\n\nThere is only one plant here, with two names. Only a call to `Plant(...)` creates a new object; an assignment never does.\n\n### One object in two places\n\nA list item and a dictionary value are references too, so one object can be in several places at once (even twice in the same list). Here each plant is in the list `garden`, and also in the dictionary `spots`, which tells where it stands:\n\n```python\nclass Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n    def __str__(self):\n        return f\"{self.name}, {self.height} cm\"\n\n\nbasil = Plant(\"Basil\")\nmint = Plant(\"Mint\")\ngarden = [basil, mint]\nspots = {\"kitchen\": basil, \"balcony\": mint}\n\nspots[\"kitchen\"].height = 18\n\nfor plant in garden:\n    print(plant)\n```\n\n```output\nBasil, 18 cm\nMint, 10 cm\n```\n\nThe basil grew through the dictionary, and the list shows it: `spots[\"kitchen\"]`, `garden[0]` and `basil` are three ways to reach one plant. However many places refer to them, the program has exactly two plants, because it calls `Plant(...)` twice.\n\n### `is` or `==`\n\nPython has two ways to ask whether two things are \"the same\". `==` asks whether two values are **equal**: do they have the same contents? The operator **`is`** asks whether they are **the very same object**, and `is not` is its opposite:\n\n```pycon\n>>> watering = {\"basil\": 2, \"mint\": 3}\n>>> schedule = {\"basil\": 2, \"mint\": 3}\n>>> plan = watering\n>>> watering == schedule\nTrue\n>>> watering is schedule\nFalse\n>>> watering is plan\nTrue\n>>> watering is not schedule\nTrue\n>>> plan[\"mint\"] = 4\n>>> watering\n{'basil': 2, 'mint': 4}\n>>> watering == schedule\nFalse\n```\n\n`watering` and `schedule` are two dictionaries that started out with the same keys and values. `plan` is not a third dictionary, only another name for `watering`, so the change made through `plan` shows up in `watering`, and the two dictionaries stop being equal.\n\nFor objects of your own classes, `==` doesn't look at the attributes. Python can't know which attributes make two plants \"equal\", so unless the class says how (a later module shows how), two objects are equal only when they are the same object. Compare the attributes you care about instead:\n\n```python\nclass Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n\nmint = Plant(\"Mint\")\nmore_mint = Plant(\"Mint\")\nprint(mint == more_mint)\nprint(mint is more_mint)\nprint(mint.name == more_mint.name)\n```\n\n```output\nFalse\nFalse\nTrue\n```\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"class Counter:\n    def __init__(self):\n        self.value = 0\n\n\na = Counter()\nb = a\nc = Counter()\nb.value += 2\nc.value += 5\nprint(a.value)\nprint(b.value)\nprint(c.value)\nprint(a is b)\n","solution":"class Counter:\n    def __init__(self):\n        self.value = 0\n\n\na = Counter()\nb = a\nc = Counter()\nb.value += 2\nc.value += 5\nprint(a.value)\nprint(b.value)\nprint(c.value)\nprint(a is b)\n","hints":["`Counter()` is called twice, so there are two counter objects. Which names refer to which?","`b = a` makes `b` a second name for the first counter, so adding 2 through `b` shows up through `a` as well.","`c` is the other counter, and `a is b` asks whether `a` and `b` are the same object."],"tests":[{"name":"Output","stdin":"","expect":"2\n2\n5\nTrue","hidden":false}],"require":[],"forbid":[],"lines":["2","2","5","True"],"more":[{"task":"The program should make two separate plants, a mint and a basil, but it prints the basil twice. Fix it so that it prints:\n\n```\nMint, 10 cm\nBasil, 25 cm\n```\n","kind":"code","seed":"class Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n    def __str__(self):\n        return f\"{self.name}, {self.height} cm\"\n\n\nmint = Plant(\"Mint\")\nbasil = mint\nbasil.name = \"Basil\"\nbasil.height = 25\n\nprint(mint)\nprint(basil)\n","solution":"class Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n    def __str__(self):\n        return f\"{self.name}, {self.height} cm\"\n\n\nmint = Plant(\"Mint\")\nbasil = Plant(\"Basil\")\nbasil.height = 25\n\nprint(mint)\nprint(basil)\n","hints":["Only a call to `Plant(...)` creates a plant. How many calls does the program have?","`basil = mint` only gives the mint plant a second name, so changing `basil` changes the mint.","Create a new object for the basil instead: `basil = Plant(\"Basil\")`."],"tests":[{"name":"Output","stdin":"","expect":"Mint, 10 cm\nBasil, 25 cm","hidden":false}],"require":[{"pattern":"\\bPlant\\s*\\(","min":2,"message":"The program needs two plant objects, so it needs two calls to `Plant(...)`."}],"forbid":[]},{"task":"Write a function `copy_of(plant)` that returns a new `Plant` object with the same name and height as the plant it gets. Changing the copy must not change the original.\n\nFor example, this code:\n\n```python\nfern = Plant(\"Fern\")\nfern.height = 30\ntwin = copy_of(fern)\ntwin.height += 5\nprint(fern)\nprint(twin)\nprint(twin is fern)\n```\n\nprints:\n\n```\nFern, 30 cm\nFern, 35 cm\nFalse\n```\n","kind":"code","seed":"class Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n    def __str__(self):\n        return f\"{self.name}, {self.height} cm\"\n\n\ndef copy_of(plant):\n    return plant\n\n\nif __name__ == \"__main__\":\n    fern = Plant(\"Fern\")\n    fern.height = 30\n    twin = copy_of(fern)\n    twin.height += 5\n    print(fern)\n    print(twin)\n    print(twin is fern)\n","solution":"class Plant:\n    def __init__(self, name):\n        self.name = name\n        self.height = 10\n\n    def __str__(self):\n        return f\"{self.name}, {self.height} cm\"\n\n\ndef copy_of(plant):\n    new_plant = Plant(plant.name)\n    new_plant.height = plant.height\n    return new_plant\n\n\nif __name__ == \"__main__\":\n    fern = Plant(\"Fern\")\n    fern.height = 30\n    twin = copy_of(fern)\n    twin.height += 5\n    print(fern)\n    print(twin)\n    print(twin is fern)\n","hints":["The starter code returns `plant` itself, which gives the caller a second name for the same object. You need a new object, so call `Plant(...)`.","The constructor takes only a name, so set the height of the new plant after creating it.","`new_plant = Plant(plant.name)`, then `new_plant.height = plant.height`, then return `new_plant`."],"tests":[{"name":"Test 1","stdin":"","call":"fern = Plant(\"Fern\")\nfern.height = 30\ntwin = copy_of(fern)\ntwin.height += 5\nprint(fern)\nprint(twin)\nprint(twin is fern)\n","expect":"Fern, 30 cm\nFern, 35 cm\nFalse","hidden":false},{"name":"Test 2","stdin":"","call":"cactus = Plant(\"Cactus\")\ncactus.height = 12\nother = copy_of(cactus)\nprint(other)\nprint(other is cactus)\n","expect":"Cactus, 12 cm\nFalse","hidden":true},{"name":"Test 3","stdin":"","call":"ivy = Plant(\"Ivy\")\ncopy = copy_of(ivy)\nivy.height = 50\ncopy.name = \"Ivy 2\"\nprint(ivy)\nprint(copy)\n","expect":"Ivy, 50 cm\nIvy 2, 10 cm","hidden":true}],"require":[],"forbid":[]}]}