Python Arena
Step 2 of 4

Two names for one object

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.

This 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:

class Plant:
    def __init__(self, name):
        self.name = name
        self.height = 10

    def __str__(self):
        return f"{self.name}, {self.height} cm"


basil = Plant("Basil")
windowsill = basil
windowsill.height += 5

print(basil)
print(windowsill)
Output
Basil, 15 cm
Basil, 15 cm

There is only one plant here, with two names. Only a call to Plant(...) creates a new object; an assignment never does.

One object in two places

A 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:

class Plant:
    def __init__(self, name):
        self.name = name
        self.height = 10

    def __str__(self):
        return f"{self.name}, {self.height} cm"


basil = Plant("Basil")
mint = Plant("Mint")
garden = [basil, mint]
spots = {"kitchen": basil, "balcony": mint}

spots["kitchen"].height = 18

for plant in garden:
    print(plant)
Output
Basil, 18 cm
Mint, 10 cm

The 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.

is or ==

Python 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:

In the Python interpreter
>>> watering = {"basil": 2, "mint": 3}
>>> schedule = {"basil": 2, "mint": 3}
>>> plan = watering
>>> watering == schedule
True
>>> watering is schedule
False
>>> watering is plan
True
>>> watering is not schedule
True
>>> plan["mint"] = 4
>>> watering
{'basil': 2, 'mint': 4}
>>> watering == schedule
False

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.

For 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:

class Plant:
    def __init__(self, name):
        self.name = name
        self.height = 10


mint = Plant("Mint")
more_mint = Plant("Mint")
print(mint == more_mint)
print(mint is more_mint)
print(mint.name == more_mint.name)
Output
False
False
True

Your task

Challenge 1 of 3

What does this program print? Type each line it prints.

Challenge 2

The program should make two separate plants, a mint and a basil, but it prints the basil twice. Fix it so that it prints:

Mint, 10 cm
Basil, 25 cm

Challenge 3

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.

For example, this code:

fern = Plant("Fern")
fern.height = 30
twin = copy_of(fern)
twin.height += 5
print(fern)
print(twin)
print(twin is fern)

prints:

Fern, 30 cm
Fern, 35 cm
False