Экземпляр живёт отдельно от переменных. Переменная — это имя, которое ссылается на объект, и имён у одного объекта может быть несколько. Пока объекты не меняются, разницы не видно. Но экземпляры своих классов обычно меняются — и тут важно понимать, сколько объектов на самом деле.
Две переменные — один объект
Присваивание 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 мы научим товары сравниваться по названию
и цене.
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
Так же работает передача в функцию: функция получает сам объект и может его изменить.
Напишите функцию raise_prices(products, amount): она увеличивает цену каждого товара из списка products
на amount рублей. Функция меняет сами товары и ничего не возвращает.
p.price = p.price + amount для каждого товара.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
def raise_prices(products, amount):
...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
def raise_prices(products, amount):
for p in products:
p.price = p.price + amountНапишите функцию find(products, name): она возвращает сам товар из списка с таким названием, а если
его нет — None.
p.name == name — return p; после цикла — return None.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
def find(products, name):
...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
def find(products, name):
for p in products:
if p.name == name:
return p
return Nonemenu — список из двух товаров, второй — чай за 120 ₽. Что напечатает код?
tea = menu[1]
tea.price = 100
print(menu[1].price)tea = menu[1] не копирует товар, а даёт объекту из списка второе имя: в menu[1] и в tea один и тот же объект. Изменение через tea видно и через список.Нужна копия — создайте новый объект
Чтобы получить независимый объект, его создают заново — вызовом класса с теми же значениями. Тогда у копии своя судьба:
В latte — товар «Латте» за 220 ₽. Создайте отдельный товар pumpkin с теми же атрибутами, затем
поменяйте ему название на "Тыквенный латте" и цену на 290. Исходный latte меняться не должен.
pumpkin = Product(latte.name, latte.price) — новый объект; потом меняйте его атрибуты.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
latte = Product("Латте", 220)
pumpkin = ...Упражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
latte = Product("Латте", 220)
pumpkin = Product(latte.name, latte.price)
pumpkin.name = "Тыквенный латте"
pumpkin.price = 290Изменяемое значение по умолчанию
Та же история со ссылками прячется в значениях по умолчанию. Список, словарь и экземпляры ваших классов — изменяемые (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
В заготовке — класс Delivery (доставка заказа) с ошибкой из демонстрации: заметки курьеру notes у всех
доставок без явного списка общие. Исправьте класс: у каждой доставки свой список notes. Если список передан
при создании, доставка хранит его копию list(notes) — чтобы её заметки не меняли чужой список.
None; в __init__ проверьте его через is None.Заготовка упражнения обновилась с тех пор, как вы сохранили свой код.
Вернуть заготовку? Ваш код в этом упражнении будет удалён.
class Delivery:
def __init__(self, address, notes=[]):
self.address = address
self.notes = notesУпражнение пока не решено, поэтому для ячеек ниже выполнено эталонное решение — ваш код не изменён.
class Delivery:
def __init__(self, address, notes=None):
self.address = address
if notes is None:
self.notes = []
else:
self.notes = list(notes)Запомните главное: присваивание и передача в функцию не копируют объекты. Копия появляется, только когда вы создаёте новый объект сами.