Один модуль Python в неделю | копировать

Python Язык программирования

Адрес столбца:Один модуль Python в неделю

В то же время, вы также можете обратить внимание на мой публичный аккаунт WeChat.AlwaysBetaболее захватывающий контент ждет вас.

copyМодуль включает в себя две функции,copy()а такжеdeepcopy(), чтобы скопировать существующий объект.

мелкая копия

copy()Созданная неглубокая копия — это новый контейнер, ссылка на содержимое исходного объекта.

import copy
import functools


@functools.total_ordering
class MyClass:

    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

    def __gt__(self, other):
        return self.name > other.name


a = MyClass('a')
my_list = [a]
dup = copy.copy(my_list)

print('             my_list:', my_list)
print('                 dup:', dup)
print('      dup is my_list:', (dup is my_list))
print('      dup == my_list:', (dup == my_list))
print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))

# output
#              my_list: [<__main__.MyClass object at 0x101f9c160>]
#                  dup: [<__main__.MyClass object at 0x101f9c160>]
#       dup is my_list: False
#       dup == my_list: True
# dup[0] is my_list[0]: True
# dup[0] == my_list[0]: True

Для мелкой копии,MyClassЭкземпляры не реплицируются, поэтомуdupа такжеmy_listотносится к одному и тому же объекту.

глубокая копия

заменить вызов наdeepcopy()сделает вывод значительно другим.

import copy
import functools


@functools.total_ordering
class MyClass:

    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

    def __gt__(self, other):
        return self.name > other.name


a = MyClass('a')
my_list = [a]
dup = copy.deepcopy(my_list)

print('             my_list:', my_list)
print('                 dup:', dup)
print('      dup is my_list:', (dup is my_list))
print('      dup == my_list:', (dup == my_list))
print('dup[0] is my_list[0]:', (dup[0] is my_list[0]))
print('dup[0] == my_list[0]:', (dup[0] == my_list[0]))

# output
#              my_list: [<__main__.MyClass object at 0x101e9c160>]
#                  dup: [<__main__.MyClass object at 0x1044e1f98>]
#       dup is my_list: False
#       dup == my_list: True
# dup[0] is my_list[0]: False
# dup[0] == my_list[0]: True

Первый элемент списка больше не является ссылкой на один и тот же объект, но при сравнении двух объектов они по-прежнему равны.

Пользовательское поведение копирования

можно использовать__copy__()а также__deepcopy__()метод настройки поведения репликации.

  • __copy__()Никаких аргументов не требуется, возвращается поверхностная копия объекта.
  • __deepcopy__()Вызывается со словарем memo и возвращает глубокую копию объекта. Любые свойства членов, требующие глубокого копирования, должны быть переданы вместе со словарем мемо вcopy.deepcopy().

В следующем примере показано, как вызвать метод.

import copy
import functools


@functools.total_ordering
class MyClass:

    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

    def __gt__(self, other):
        return self.name > other.name

    def __copy__(self):
        print('__copy__()')
        return MyClass(self.name)

    def __deepcopy__(self, memo):
        print('__deepcopy__({})'.format(memo))
        return MyClass(copy.deepcopy(self.name, memo))


a = MyClass('a')

sc = copy.copy(a)
dc = copy.deepcopy(a)

# output
# __copy__()
# __deepcopy__({})

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

рекурсия в глубоком копировании

Чтобы избежать проблемы повторения рекурсивных структур данных,deepcopy()Используйте словарь для отслеживания скопированных объектов. Этот словарь передается__deepcopy__()метод, поэтому здесь можно проверить повторяющиеся проблемы рекурсии.

Следующий пример показывает, как взаимосвязанная структура данных, такая как ориентированный граф, может быть реализована с помощью__deepcopy__()метод для предотвращения рекурсии.

import copy


class Graph:

    def __init__(self, name, connections):
        self.name = name
        self.connections = connections

    def add_connection(self, other):
        self.connections.append(other)

    def __repr__(self):
        return 'Graph(name={}, id={})'.format(
            self.name, id(self))

    def __deepcopy__(self, memo):
        print('\nCalling __deepcopy__ for {!r}'.format(self))
        if self in memo:
            existing = memo.get(self)
            print('  Already copied to {!r}'.format(existing))
            return existing
        print('  Memo dictionary:')
        if memo:
            for k, v in memo.items():
                print('    {}: {}'.format(k, v))
        else:
            print('    (empty)')
        dup = Graph(copy.deepcopy(self.name, memo), [])
        print('  Copying to new object {}'.format(dup))
        memo[self] = dup
        for c in self.connections:
            dup.add_connection(copy.deepcopy(c, memo))
        return dup


root = Graph('root', [])
a = Graph('a', [root])
b = Graph('b', [a, root])
root.add_connection(a)
root.add_connection(b)

dup = copy.deepcopy(root)

# output
# Calling __deepcopy__ for Graph(name=root, id=4326183824)
#   Memo dictionary:
#     (empty)
#   Copying to new object Graph(name=root, id=4367233208)
# 
# Calling __deepcopy__ for Graph(name=a, id=4326186344)
#   Memo dictionary:
#     Graph(name=root, id=4326183824): Graph(name=root, id=4367233208)
#   Copying to new object Graph(name=a, id=4367234720)
# 
# Calling __deepcopy__ for Graph(name=root, id=4326183824)
#   Already copied to Graph(name=root, id=4367233208)
# 
# Calling __deepcopy__ for Graph(name=b, id=4326183880)
#   Memo dictionary:
#     Graph(name=root, id=4326183824): Graph(name=root, id=4367233208)
#     Graph(name=a, id=4326186344): Graph(name=a, id=4367234720)
#     4326183824: Graph(name=root, id=4367233208)
#     4367217936: [Graph(name=root, id=4326183824), Graph(name=a, id=4326186344)]
#     4326186344: Graph(name=a, id=4367234720)
#   Copying to new object Graph(name=b, id=4367235000)

GraphКласс включает в себя несколько основных методов ориентированного графа. И вы можете использовать существующий экземпляр инициализации списка имен узлов, подключенный к нему.add_connection()Метод используется для установки двунаправленного соединения. Он также используется оператором глубокого копирования.

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

digraph copy_example {“root”;  “a” - >“root”;  “b” - >“root”;  “b” - >“a”;  “root” - >“a”;  “root” - >“b”;  }

Глубокая копия графа объектов с циклами

Граф, показанный на рисунке, включает несколько циклов, но использование мемо-словаря для обработки рекурсии предотвращает возникновение ошибок переполнения стека при обходе.

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

Связанные документы:

Дешевый Мо Corruption.com/3/copy/in the…