Нашли ошибку?
Скачать чистый
урок 2 из 3≈ 20 минупражнения: 0 из 4

Объекты в списках и ссылки

Переменная не хранит объект, а ссылается на него. Две переменные — один объект, оператор is, изменение через ссылку, объекты в списках и изменяемое значение по умолчанию.

Python запустится при первом выполнении

Экземпляр живёт отдельно от переменных. Переменная — это имя, которое ссылается на объект, и имён у одного объекта может быть несколько. Пока объекты не меняются, разницы не видно. Но экземпляры своих классов обычно меняются — и тут важно понимать, сколько объектов на самом деле.

Две переменные — один объект

Присваивание b = a не копирует объект: оно даёт тому же объекту второе имя. Изменение через одно имя видно через другое:

[ ]Второе имя для того же товара
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price


latte = Product("Латте", 220)
same = latte  # второе имя, не копия
same.price = 250
print(latte.price)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
250

Проверить, один ли это объект, можно оператором is. Он отвечает на вопрос о тождественности (identity) — «тот же самый объект?», — а == — о равенстве (equality). Для своих классов == по умолчанию тоже сравнивает тождественность, но это можно изменить — в модуле 4 мы научим товары сравниваться по названию и цене.

[ ]is: тот же объект или нет
twin = Product("Латте", 250)  # новый объект с теми же значениями
print(same is latte)
print(twin is latte)
print(twin == latte)  # пока == сравнивает тождественность
print(twin.price == latte.price)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
True
False
False
True

Самое частое место для is — проверка на None: if x is None:. Значение None в программе одно, поэтому его сравнивают по тождественности.

Объекты в списках и словарях

Список хранит не копии объектов, а ссылки на них. Если взять товар из списка и изменить, изменится тот же объект, на который ссылается переменная снаружи:

[ ]Товар в меню и в переменной
espresso = Product("Эспрессо", 150)
menu = [espresso, Product("Чай", 120)]
menu[0].price = 160
print(espresso.price)
print(menu[0] is espresso)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
160
True

Так же работает передача в функцию: функция получает сам объект и может его изменить.

[ ]Упражнение 1Подорожание решено

Напишите функцию raise_prices(products, amount): она увеличивает цену каждого товара из списка products на amount рублей. Функция меняет сами товары и ничего не возвращает.

def raise_prices(products, amount):
    ...

[ ]Упражнение 2Найти товар решено

Напишите функцию find(products, name): она возвращает сам товар из списка с таким названием, а если его нет — None.

def find(products, name):
    ...

Вопрос

menu — список из двух товаров, второй — чай за 120 ₽. Что напечатает код?

tea = menu[1]
tea.price = 100
print(menu[1].price)

Нужна копия — создайте новый объект

Чтобы получить независимый объект, его создают заново — вызовом класса с теми же значениями. Тогда у копии своя судьба:

[ ]Упражнение 3Сезонный напиток решено

В latte — товар «Латте» за 220 ₽. Создайте отдельный товар pumpkin с теми же атрибутами, затем поменяйте ему название на "Тыквенный латте" и цену на 290. Исходный latte меняться не должен.

latte = Product("Латте", 220)
pumpkin = ...

Изменяемое значение по умолчанию

Та же история со ссылками прячется в значениях по умолчанию. Список, словарь и экземпляры ваших классов — изменяемые (mutable) объекты: их можно поменять, не создавая новый объект. Числа, строки и кортежи — неизменяемые (immutable). Значение по умолчанию вычисляется один раз, когда объявляется функция, а не при каждом вызове. Если это список, все вызовы без аргумента получат один и тот же список:

[ ]Два заказа с одним списком
class Ticket:
    def __init__(self, customer, items=[]):  # ошибка: один список на все вызовы
        self.customer = customer
        self.items = items


anna = Ticket("Анна")
boris = Ticket("Борис")
anna.items.append("Латте")
print(boris.items)
print(anna.items is boris.items)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
['Латте']
True
[ ]Упражнение 4Починить доставку решено

В заготовке — класс Delivery (доставка заказа) с ошибкой из демонстрации: заметки курьеру notes у всех доставок без явного списка общие. Исправьте класс: у каждой доставки свой список notes. Если список передан при создании, доставка хранит его копию list(notes) — чтобы её заметки не меняли чужой список.

class Delivery:
    def __init__(self, address, notes=[]):
        self.address = address
        self.notes = notes

Запомните главное: присваивание и передача в функцию не копируют объекты. Копия появляется, только когда вы создаёте новый объект сами.

Нашли ошибку?