And can you keep your head, your backbone or your heart? We all found out the answer on the day it fell apart.
Вчера немного задолбался дебагать кусок рабочего кода из-за одной характерной специфики Пайтона. Оказывается, операция присвоения для списка не копирует список
Демонстрирую:
# Объявляем список
a = [10, 20, 30]
# Присваиваем новой переменной этот список
b = a
# Убираем из нового списка значение
b.remove(20)
# Выводим исходный список
print a
# Получаем на выходе, медленно хренеем.
[10, 30]
То есть, присвоение для списков (тьюплов и словарей; и вообще много где) не создаёт новый список, а просто ставит линк на старый.
Это ВНЕЗАПНАЯ новость.
Демонстрирую:
# Объявляем список
a = [10, 20, 30]
# Присваиваем новой переменной этот список
b = a
# Убираем из нового списка значение
b.remove(20)
# Выводим исходный список
print a
# Получаем на выходе, медленно хренеем.
[10, 30]
То есть, присвоение для списков (тьюплов и словарей; и вообще много где) не создаёт новый список, а просто ставит линк на старый.
Это ВНЕЗАПНАЯ новость.
Но может я неправильно помню.
Ты бы почитал мануал на тему, а то еще не раз тебе придется задалбываться при дебаге)
То странно, что я такое только сейчас встретил, хотя похожего кода писал не раз. Но, впрочем, кажется там было нормальное клонирование. Надо посмотреть.
Всё там на месте Поведение будет абсолютно таким же.
Чтобы каждый раз не аллокейтить память под переменную при любом чихе,
Здесь несколько иная мотивация, нежели просто не аллокейтить "лишнюю" память. Если бы можно было - аллокейтили бы, очень удобно же. Но универсальное глубокое копирование из коробки - это a big problem, его нет почти нигде (из коробки).
Проблема, если кратко, в том как корректно скопировать какой-нибудь сложный объект. А массив - это сложный объект. Это у тебя тут конкретно простой массив интов, а если это массив классов с делегатами внутри? И делегат ссылается на метод внутри класса. Куда должен ссылаться делегат скопированного класса? На метод скопированного класса, скажете вы, и будете правы, но реализовать это очень нетривиально. А если в классе, экземпляры которого лежат в массиве, поле вида структуры, у которой поле в виде структуры {и так далее}, то ведь надо рекурсивно по всем пробежаться, а это тоже гемор тот ещё.
Короче говоря, практически всегда operator= для не примитивных типов это копирование ссылки(указателя). Иногда (в новом с++, например, так можно) - это перемещение ссылки(указателя) из старой переменной в новую. А "глубокое копирование" - это то, что пишется под свои нужды и работает ограниченно. обычно вместо этого используют сериализацию, а потом восстанавливают сериализованный объект, но это уже совсем другая история...
В твоём случае ситуация следующая:
docs.python.org/2/reference/simple_stmts.html#a...
«Assignment of an object to a single target is recursively defined as follows.
If the target is an identifier (name):
If the name does not occur in a global statement in the current code block: the name is bound to the object in the current local namespace.
Otherwise: the name is bound to the object in the current global namespace.
The name is rebound if it was already bound.»
Как видишь, в случае с объектами, оператор присваивания перебайнживает то, куда указывает имя по левую сторону от оператора. То есть это очень приблизительно reference assignment, как в джаве или C#. Понятно что ни о каком deep-copy при такой семантике речи быть не может.