Копирование массива в Python. Метод copy для копирования массива. Представление массива
В этой статье мы поговорим, как скопировать массив в Python и чем копирование массива отличается от представления массива. Также рассмотрим поверхностное и глубокое копирование объектов в Python.
Работая с массивами в Python, вы наверняка сталкивались с ситуацией, когда при использовании некоторых функций, возвращающих какой-нибудь результат, с исходным массивом не происходит ничего. Как в примере ниже:
Всё дело в том, что в библиотеке NumPy есть два понятия касаемо массива: копия и представление. И это разные вещи. Посмотрите на код ниже:
На самом деле, всё просто. Когда мы выполняем присваивание b = a, никакого копирования данных на деле не происходит. В памяти компьютера всё так же один массив, а переменные a и b — это даже не переменные, а указатели, указывающие на одни и те же данные. Таким образом, мы обращаемся по разным указателям к одним и тем же данным в памяти и видим в результате одно и то же.
Хорошо, a и b являются указателями, но что тогда с переменной с? На деле, это тоже указатель, ссылающийся на ту же область памяти с данными, правда, представлены эти данные в иной форме. Вот мы и подошли к понятию представления массива, которое существует в NumPy. Действительно, те же данные можно представить в разной форме:
На деле, можно говорить о копировании, если данные в памяти компьютера физически копируются в другое место. Но, как мы уже убедились, операция присваивания копирование не выполняет. Также мы поняли, что те же самые данные могут иметь различные представления.
Присваивание не копирует массивы в Python
Итак, простое присваивание никаких копий массива не выполняет, и это первое, что стоит уяснить. Может показаться, что это всего лишь прихоть создателей Python и NumPy, но всё не так просто. Если бы ситуация обстояла иначе, мы бы работали с памятью напрямую, а отсутствие автоматического копирования во время присваивания — совсем небольшая плата за лёгкость и простоту языка программирования Python.
Давайте приведём ещё парочку примеров на эту тему:
Обратите внимание, что массивы a и b в действительности являются одним и тем же массивом с такими же данными и типом данных.
Таким образом, у нас есть массив b и массив a, но нельзя забывать о том, что это, по сути, один и тот же массив.
Так как же копировать массивы в Python?
Чтобы сделать полную копию массива в Python, используют метод copy. Он выполняет копирование не только данных массива, но и всех его свойств.
После применения метода copy мы можем говорить о массивах a и b, как о разных массивах и копиях друг друга. Да, эти массивы имеют одинаковые данные, но эти данные не являются одними и теми же. Теперь действительно массив b является копией массива a, и именно это называется копированием массива в терминологии NumPy.
Представление массива
Итак, теперь мы знаем, как сделать копию массива посредством метода copy. Но бывают ситуации, когда нам не нужна копия массива, а нужен тот же массив но с иными размерами. Речь идёт, как вы уже догадались, о другом представлении исходного массива.
Для этих целей в NumPy есть метод
Обычно, функции которые меняют форму и порядок элементов в Пайтон-массивах возвращают не копию массива, а именно его представление:
Также представлениями массивов в Python являются срезы массивов:
Обратите внимание, что когда мы говорим о том, что массив b является представлением массива a, мы подразумеваем, что вне зависимости от вида и формы массива b он включает в себя те же данные в памяти, что и наш массив a. Таким образом, изменение элементов в одном из массивов приведёт, соответственно, к изменениям в другом.
Глубокое и поверхностное копирование объектов с помощью copy
Как мы уже хорошо уяснили, операция присваивания не приводит к копированию объекта, а лишь создаёт ссылку на этот объект. Но если мы работаем с изменяемыми коллекциями или коллекциями, которые содержат изменяемые элементы, нам может понадобиться такая копия, которую мы сможем изменить, не меняя оригинал. Здесь нам тоже поможет copy, выполняющий как поверхностное, так и глубокое копирование: • copy.copy(a) — возвращает поверхностную копию a; • copy.deepcopy(a) — возвращает полную копию a.
Если же объект скопировать невозможно, возникает исключение copy.error. В принципе, разница между глубоким и поверхностным копированием существенна лишь для составных объектов, которые содержат изменяемые объекты (допустим, список списков). При этом: 1) поверхностная копия позволяет создать новый составной объект, а потом (если это возможно) вставляет в него ссылки на объекты, которые находятся в оригинале; 2) глубокая копия позволяет создать новый составной объект, а потом рекурсивно вставляет в него копии объектов, которые находятся в оригинале.
>>> import copy >>> test_1 = [1, 2, 3, [1, 2, 3]] >>> test_copy = copy.copy(test_1) >>> print(test_1, test_copy) [1, 2, 3, [1, 2, 3]] [1, 2, 3, [1, 2, 3]] >>> test_copy[3].append(4) >>> print(test_1, test_copy) [1, 2, 3, [1, 2, 3, 4]] [1, 2, 3, [1, 2, 3, 4]] >>> test_1 = [1, 2, 3, [1, 2, 3]] >>> test_deepcopy = copy.deepcopy(test_1) >>> test_deepcopy[3].append(4) >>> print(test_1, test_deepcopy) [1, 2, 3, [1, 2, 3]] [1, 2, 3, [1, 2, 3, 4]]
При выполнении глубокого копирования возможны проблемы (их нет у поверхностного копирования): — рекурсивные объекты могут привести к рекурсивному циклу; — т. к. глубокая копия копирует всё, она способна скопировать слишком много, к примеру, административные структуры данных.
Однако в случае возникновения проблем нам поможет функция deepcopy, которая устраняет эти сложности: — посредством хранения "memo" словаря объектов; — позволяя классам, которые определяет пользователь, переопределять операцию копирования либо набор копируемых компонентов.
>>> r = [1, 2, 3] >>> r.append(r) >>> print(r) [1, 2, 3, [...]] >>> p = copy.deepcopy(r) >>> print(p) [1, 2, 3, [...]]
В результате, не копируются типы вроде классов, функций, модулей, методов, стековых кадров, окон, сокетов и т. п.
Что же, теперь, надеемся, вы получили представление о копировании массивов и объектов в Python. Если хотите знать больше, к вашим услугам специализированный курс для продвинутых разработчиков:
При написании материала использовались статьи: — «Модуль copy — поверхностное и глубокое копирование объектов»; — «Копии и представления массивов».