Как я могу создать копию объекта в Python? - PullRequest
148 голосов
/ 25 января 2011

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

Ответы [ 4 ]

141 голосов
/ 25 января 2011

Чтобы получить полностью независимую копию объекта, вы можете использовать функцию copy.deepcopy().

Подробнее о мелком и глубоком копировании см. В других ответах на этот вопрос.и хорошее объяснение в этом ответе на связанный вопрос .

48 голосов
/ 25 октября 2017

Как я могу создать копию объекта в Python?

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

Тогда вы имеете в виду изменяемый объект.

В Python 3 списки получают метод copy (в 2 вы бы использовали фрагмент для создания копии):

>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True

Мелкие копии

Мелкие копии - это всего лишь копии самого внешнего контейнера.

list.copy это мелкая копия:

>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

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

Глубокие копии

Глубокие копии - это рекурсивные копии каждого предмета интерьера.

>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

Изменения не отражаются в оригинале, только в копии.

Неизменяемые объекты

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

>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'

У кортежей даже нет метода копирования, поэтому давайте попробуем его с фрагментом:

>>> tuple_copy_attempt = a_tuple[:]

Но мы видим, что это тот же объект:

>>> tuple_copy_attempt is a_tuple
True

Аналогично для строк:

>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True

и для Frozensets, хотя у них есть метод copy:

>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True

Когда копировать неизменяемые объекты

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

>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)

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

Пользовательские объекты

Пользовательские объекты обычно хранят данные в атрибуте __dict__ или в __slots__ (структура памяти, подобная кортежу).

Чтобы создать копируемый объект, определите __copy__ (для мелких копий) и / или __deepcopy__ (для глубоких копий).

from copy import copy, deepcopy

class Copyable:
    __slots__ = 'a', '__dict__'
    def __init__(self, a, b):
        self.a, self.b = a, b
    def __copy__(self):
        return type(self)(self.a, self.b)
    def __deepcopy__(self, memo): # memo is a dict of id's to copies
        id_self = id(self)        # memoization avoids unnecesary recursion
        _copy = memo.get(id_self)
        if _copy is None:
            _copy = type(self)(
                deepcopy(self.a, memo), 
                deepcopy(self.b, memo))
            memo[id_self] = _copy 
        return _copy

Обратите внимание, что deepcopy сохраняет словарь для напоминаний id(original) (или идентификационные номера) для копий. Чтобы получить хорошее поведение с рекурсивными структурами данных, убедитесь, что вы еще не сделали копию, и, если она у вас есть, верните ее.

Итак, давайте сделаем объект:

>>> c1 = Copyable(1, [2])

И copy делает мелкую копию:

>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]

И deepcopy теперь делает глубокую копию:

>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]
6 голосов

Мелкая копия с copy.copy()

#!/usr/bin/env python3

import copy

class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]

# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]

# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]

Глубокая копия с copy.deepcopy()

#!/usr/bin/env python3
import copy
class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]

Документация: https://docs.python.org/3/library/copy.html

Проверено на Python 3.6.5.

0 голосов
/ 21 февраля 2018

Я полагаю, что следующее должно работать со многими классифицированными в Python классами:

def copy(obj):
    return type(obj)(obj)

(Конечно, я не говорю здесь о "глубоких копиях", это отдельная история, и которая может быть не очень ясным понятием - насколько глубокая глубина?)

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

Конечно, этот метод работает только для классов, конструкторы которых ведут себя соответственно. Возможные варианты использования: создание мелкой копии стандартного класса контейнера Python.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...