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]

То есть, присвоение для списков (тьюплов и словарей; и вообще много где) не создаёт новый список, а просто ставит линк на старый.
Это ВНЕЗАПНАЯ новость.

@темы: webweaving

Комментарии
16.03.2016 в 12:54

I have got no kingdom. In this world I'm through.
Не думаю что это специфика пайтона, ведь коллекции обычно используют для динамически изменяющихся значений и их количества в рантайме. Чтобы каждый раз не аллокейтить память под переменную при любом чихе, гораздо удобнее хранить ссылку на кучу. Совершенно логично что переменным ссылочного типа присваиваются тоже ссылки, а не значения по ссылкам =)
16.03.2016 в 12:59

And can you keep your head, your backbone or your heart? We all found out the answer on the day it fell apart.
Night de Lune, ну вот в сях, когда я последний раз трогал си, такого подъёба кажется не было.
Но может я неправильно помню.
16.03.2016 в 13:30

I have got no kingdom. In this world I'm through.
Tengro, ну, си это си, я тут промолчу. А вот в C# и в джаве, емнип, вышеописанная картина. В С# четкое разделение на типы значения (все числа, символы, булеан, enum и struct) и ссылочные типы (классы, интерфейсы, делегаты). Кстати поскольку string это коллекция чаров, он ссылочного типа, и его присвоение тоже по ссылке идет) там уйма всякой специфики связанная с присвоением, специальный интерфейс ICloneable для клонирования объектов, несколько функций клонирования, включая MemberwiseClone(), чтобы если у тебя в классе поле ссылочного типа, то чтобы при копировании всего внешнего объекта не получилось так что поля копии и оригинала указывают на один и тот же объект, а для поля тоже создалась соответствующая копия.
Ты бы почитал мануал на тему, а то еще не раз тебе придется задалбываться при дебаге)
16.03.2016 в 13:33

And can you keep your head, your backbone or your heart? We all found out the answer on the day it fell apart.
Night de Lune, надо освежить в памяти пайтоновскую доку, на самом деле.
То странно, что я такое только сейчас встретил, хотя похожего кода писал не раз. Но, впрочем, кажется там было нормальное клонирование. Надо посмотреть.
17.03.2016 в 01:36

Свет состоит из радуг, мир состоит из встреч.
ну вот в сях, когда я последний раз трогал си, такого подъёба кажется не было.

Всё там на месте :) Поведение будет абсолютно таким же.

Чтобы каждый раз не аллокейтить память под переменную при любом чихе,

Здесь несколько иная мотивация, нежели просто не аллокейтить "лишнюю" память. Если бы можно было - аллокейтили бы, очень удобно же. Но универсальное глубокое копирование из коробки - это a big problem, его нет почти нигде (из коробки).

Проблема, если кратко, в том как корректно скопировать какой-нибудь сложный объект. А массив - это сложный объект. Это у тебя тут конкретно простой массив интов, а если это массив классов с делегатами внутри? И делегат ссылается на метод внутри класса. Куда должен ссылаться делегат скопированного класса? На метод скопированного класса, скажете вы, и будете правы, но реализовать это очень нетривиально. А если в классе, экземпляры которого лежат в массиве, поле вида структуры, у которой поле в виде структуры {и так далее}, то ведь надо рекурсивно по всем пробежаться, а это тоже гемор тот ещё.

Короче говоря, практически всегда operator= для не примитивных типов это копирование ссылки(указателя). Иногда (в новом с++, например, так можно) - это перемещение ссылки(указателя) из старой переменной в новую. А "глубокое копирование" - это то, что пишется под свои нужды и работает ограниченно. обычно вместо этого используют сериализацию, а потом восстанавливают сериализованный объект, но это уже совсем другая история...
17.03.2016 в 10:14

Colorless green ideas sleep furiously
Tengro, Мегакоты дело говорят. Ты наверно спутал си с плюсами: в сях я вообще не представляю как заставить = работать как deep-copy (там же ж перегрузки операторов, в отличии от плюсов, нету).

В твоём случае ситуация следующая:
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 при такой семантике речи быть не может.
17.03.2016 в 11:15

Свет состоит из радуг, мир состоит из встреч.
Self-mad, в качестве утреннего безумия, в сях заставить равно работать иначе наверняка можно каким-нибудь хитрым макросом define. Но это уже из разряда мегазла =)

Расширенная форма

Редактировать

Подписаться на новые комментарии