Comments 9
Благодаря динамической природе Python, в нём не видна суть кортежа - благодаря этому он воспринимается как "просто неизменяемый список". Однако суть его совсем в другом, и это видно только в статических языках, например в Rust.
Я воспринимаю эту разницу так: список - это изменяемая последовательность однородных данных, кортеж - это неизменяемый набор разнородных данных. Он потому неизменяемый, так как данные разнородны и в статических языках мы не можем заменить тип элемента кортежа. В Python эта разница размывается, так как в списке могут быть ссылки на объекты разных типов.
В этой статье этот момент упоминался:
Используйте кортеж, когда данные — это структура
На мой взгляд, это основное предназначение кортежа, как типа данных. Но в Python его, конечно же, можно использовать и в качестве неизменяемого списка.
ИМХО. Критика приветствуется.
my_tuple = my_tuple + (4,)Старый объект
(1, 2, 'привет')остался нетронутым (и вскоре будет удален сборщиком мусора).
Извините за глупый вопрос. Если не брать вопрос производительности, то в чем тогда польза неизменяемости? Если старый объект все равно будет удален и мы больше не можем его использовать.
Вопрос производительности один из главных вопросов)
Польза в том, что где-то в другом месте могут быть ссылки на это же самое значение. И вот такое внезапное переписывание сломает всё.
x = 123
y = x
y += 456 # фактически, y = y + 4,5,6
assert x == 123
x = '123'
y = x
y += '456' # тоже замена объекта, y = y + '456'
assert x == '123'
x = (1,2,3)
y = x
y += (4,5,6) # тоже замена объекта
assert x == (1,2,3)
x = [1,2,3]
y = x
y += [4,5,6] # а вот у list это автоинкремент!
assert x == [1,2,3,4,5,6] # wtf, да?
assert id(x) == id(y)Семантика значений предполагает неразличимость одинаковых экземпляров между собой. Семантика объектов - различает экземпляры по адресу. (Хотя сравнивать значения объектов никто не мешает).
Разные переменные могут ссылаться на один и тот же экземпляр.
В случае значений ссылка на общий экземпляр - это всего лишь оптимизация, и при изменении нужно выполнять copy on write. В случае объектов - это уже обязанность, заложенная в семантику.
x = SomeObject() # создали объект "в воздухе" и затем запомнили его в x
y = x # и его же (а не его копию) запомнили в y
x.DoSomething() # этот объект что-то делает
y.DoSomethingElse() # тот же самый объект делает что-то другоеЖизнь с семантикой только объектов - в принципе, возможна, но писанины при этом будет больше. Сишные строковые буферы со всеми этими strdup, strcpy, strcat мало людям крови попили?
Другое дело, что, когда в языке обе семантики сосуществуют, да ещё и в одном синтаксисе, - новичка это может знатно смутить, а также подвигнуть на написание багов.
Самый популярный баг - это дефолтные аргументы функций с семантикой изменяемого объекта. Опытные по этим граблям прошлись, поэтому дефолтят к чему-нибудь неизменяемому, например, None, а внутри функции ветвятся.
В Python все является объектом, и переменные - это всегда ссылки на объекты. Разница между "изменяемыми" и "неизменяемыми" типами заключается в том, что происходит при "изменении", ваши примеры это хорошо показывают.
"Семантика объектов - различает экземпляры по адресу" верно по умолчанию. Но есть ещёeq.
Так же тут не раскрыли тему с hash
Литералы тоже являются объектами.
Вот только для поведения программы - если, разве что, не упороться по сравниванию их id'ов, - это не имеет значения.
Например, маленькие целые числа в питоне идентифицируют сами себя. Одинаковые значения - одинаковые id'ы. (Если бы это было не так, это была бы адская просадка производительности). А большие целые числа лежат в памяти, и два одинаковых могут лежать в разных местах.
В этом и есть главное отличие значений от "обычных" объектов. Нам плевать на детали реализации, значения между собой неразличимы для нас. Поэтому они могут мигрировать как угодно по памяти - копироваться, кэшироваться, даже вычисляться на лету.
Объект в ООП - это сущность с идентичностью, поведением и состоянием. Раз нам идентичность нужна, тогда мы вынуждены заморозить состояние. Можно наоборот - состояний много, но идентичность пропадает, это техника copy on write. Ну и поведение - либо не трогает идентичность (не переселяет объект), либо не трогает состояние.
Сравнивать текущие состояния объектов - не проблема. Проблема - использовать изменчивые объекты в качестве ключей в словарях. Поэтому волюнтаристским образом договариваются: если у объекта нет функций, изменяющих его, то только тогда мы ассоциируем с ним функцию нахождения хеша его состояния (сиречь, его значения) - добавляем метод в его класс. В C++ делают по-другому, там можно наложить константность на тип. (Эту константность, конечно, тоже можно хакнуть и сделать программе больно, - но зачем).
Зачем ты frozenset инициализируешь списком? Какая религия запрещает инициализировать его таплом? Особенно, с данными примерами, когда аргумент — константное выражение.
Ещё по теме: почему бы не упомянуть в статье, что пустой тапл и пустой frozenset в CPython — это синглтоны?
Извините, не смог сдержаться
ALLOWED_ITEM_CODES = (101, 203, 404, 550, 812, ...)
defprocess_items(items):
foriteminitems:
ifitem.codeinALLOWED_ITEM_CODES: # Эта проверка будет чуть быстрее # ... делаем что-то важное ...
Для ALLOWED_ITEM_CODES лучше использовать set и проверять вхождение не перебором, а дёргать по ключу. Раз уж мы хотим оптимизации и скорости, то лучше так.
Однозначно (¡)
Python для начинающих: () или []? Ответ, который изменит ваш код