Шаг 69.
Python: тонкости программирования.
Классы и ООП. Клонирование объектов. Копирование произвольных объектов

    На этом шаге мы рассмотрим особенности реализации этой операции.

    Вопрос, на который мы по-прежнему должны ответить, состоит в том, как создавать копии (мелкие и глубокие) произвольных объектов, включая собственные классы. Теперь давайте обратимся к этому вопросу.

    И снова на выручку приходит модуль copy. Его функции copy.copy() и copy.deepcopy() могут использоваться для создания дубликата любого объекта.

    И снова наилучший способ понять, как их использовать, - поставить простой эксперимент. Возьмем за основу предыдущий пример с копированием списка. Давайте начнем с определения простого класса двумерной точки:

>>> class Point:
	def __init__(self, x, y):
		self.x = x
		self.y = y
	def __repr__(self):
		return f'Point({self.x!r}, {self.y!r})'

    Надеюсь, вы согласитесь, что это было довольно прямолинейно. Мы добавили реализацию __repr__(), с тем чтобы мы могли легко проинспектировать создаваемые на основе этого класса объекты в интерпретаторе Python.

    Далее мы создадим экземпляр Point, а затем его (мелко) скопируем, использовав модуль copy:

>>> a = Point(23, 42)
>>> b = copy.copy(a)

    Если проинспектировать содержимое оригинального объекта Point и его (мелкого) клона, то мы увидим то, что и ожидали:

>>> a
Point(23, 42)
>>> b
Point(23, 42)
>>> a is b
False

    Следует иметь в виду кое-что еще. Поскольку наш объект-точка для своих координат использует примитивные типы (целые числа), то в данном случае нет никакой разницы между мелкой и глубокой копией. Но мы расширим пример секунду спустя.

    Теперь перейдем к более сложному примеру. Определим еще один класс, который будет представлять двумерные прямоугольники. Сделаем это таким образом, который позволяет создавать более сложную иерархию объектов, - прямоугольники будут использовать объекты Point, представляющие их координаты:

>>> class Rectangle:
	def __init__(self, topleft, bottomright):
		self.topleft = topleft
		self.bottomright = bottomright
	def __repr__(self):
		return (f'Rectangle({self.topleft!r},'
			f'{self.bottomright!r})')

    Сначала мы попытаемся создать мелкую копию экземпляра Rectangle:

>>> rect = Rectangle(Point(0, 1), Point(5, 6))
>>> srect = copy.copy(rect)

    Если вы проинспектируете оригинальный прямоугольник и его копию, то увидите, что переопределение метода __repr__() прекрасно сработало и процесс мелкого копирования был выполнен, как мы и ждали:

>>> rect
Rectangle(Point(0, 1),Point(5, 6))
>>> srect
Rectangle(Point(0, 1),Point(5, 6))
>>> rect is srect
False

    Помните, как в предыдущем примере со списком иллюстрировалась разница между глубокими и мелкими копиями? Здесь мы собираемся применить тот же самый подход. Изменим объект, находящийся глубоко в иерархии объектов, а затем вы вновь увидите, как это изменение будет отражено в (мелкой) копии:

>>> rect.topleft.x = 999
>>> rect
Rectangle(Point(999, 1),Point(5, 6))
>>> srect
Rectangle(Point(999, 1),Point(5, 6))

    Надеемся, что этот пример показал то, что вы ожидали. Далее, создадим глубокую копию оригинального прямоугольника. Затем внесем в нее одно изменение, и вы увидите, какие объекты были затронуты:

>>> drect = copy.deepcopy(srect)
>>> drect.topleft.x = 222
>>> drect
Rectangle(Point(222, 1),Point(5, 6))
>>> rect
Rectangle(Point(999, 1),Point(5, 6))
>>> srect
Rectangle(Point(999, 1),Point(5, 6))

    Вуаля! На этот раз глубокая копия (drect) полностью независима от оригинала (rect) и мелкой копии (srect).

    В этих шагах мы рассмотрели многие вопросы, и при этом остались еще некоторые тонкости, связанные с копированием объектов.

    Эта тема стоит того, чтобы в нее углубиться (еще бы!), поэтому, возможно, вам стоит плотнее заняться документацией модуля copy.


См. документацию Python "Операции мелкого и глубокого копирования": https://docs.python.org/3/library/copy.html.

    Например, объекты могут управлять тем, как они копируются, если в них определить специальные методы __copy__() и __deepcopy__(). Приятного времяпрепровождения!

    На следующем шаге мы подитожим изученный материал.




Предыдущий шаг Содержание Следующий шаг