Hoe kan ik een kopie van een object in Python maken?

Ik wil graag een kopie van een object maken. Ik wil dat het nieuwe object alle eigenschappen van het oude object (waarden van de velden) bezit. Maar ik wil onafhankelijke objecten hebben. Dus, als ik de waarden van de velden van het nieuwe object wijzig, mag het oude object daar niet door worden beïnvloed.


Antwoord 1, Autoriteit 100%

Om een ​​volledig onafhankelijke kopie van een object te krijgen, kunt u de copy.deepcopy()functie.

Raadpleeg de andere antwoorden op deze vraag voor meer informatie over ondiep en diepe kopiëren en de leuke uitleg in Dit antwoord op een gerelateerde vraag .


Antwoord 2, Autoriteit 42%

Hoe kan ik een kopie van een object in Python maken?

Dus, als ik waarden van de velden van het nieuwe object wijzig, mag het oude object daar niet door worden beïnvloed.

U bedoelt dan een mutable-object.

In Python 3, lijsten krijgen een copymethode (in 2, u zou een plak gebruiken om een ​​kopie te maken):

>>> 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

Ondiepe exemplaren

Ondiepe exemplaren zijn alleen kopieën van de buitenste container.

list.copyis een ondiepe kopie:

>>> 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'}}]

Je krijgt geen kopie van de interieurobjecten. Ze zijn hetzelfde object – dus als ze gemuteerd zijn, verschijnt de wijziging in beide containers.

Diepe kopieën

Diepe kopieën zijn recursieve kopieën van elk interieurobject.

>>> 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'}}]

Wijzigingen worden niet weergegeven in het origineel, alleen in de kopie.

Onveranderlijke objecten

Onveranderlijke objecten hoeven meestal niet te worden gekopieerd. Sterker nog, als je het probeert, zal Python je gewoon het originele object geven:

>>> 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'

Tuples hebben niet eens een kopieermethode, dus laten we het proberen met een plak:

>>> tuple_copy_attempt = a_tuple[:]

Maar we zien dat het hetzelfde object is:

>>> tuple_copy_attempt is a_tuple
True

Hetzelfde geldt voor strings:

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

en voor frozensets, ook al hebben ze een copymethode:

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

Wanneer u onveranderbare objecten wilt kopiëren

onveranderlijke objecten moeten worden gekopieerd als u een gekopieerd mutaboratoriumobject nodig heeft.

>>> 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'],)

Zoals we kunnen zien, wanneer het interieurobject van de kopie is gemuteerd, doet het origineel niet wijziging.

Aangepaste objecten

Aangepaste objecten slaan gewoonlijk gegevens op in een __dict__ATTIBUTE of IN __slots__(een tuple-achtige geheugenstructuur.)

Om een ​​kopieerbaar object te maken, definieert u __copy__(voor ondiepe exemplaren) en / of __deepcopy__(voor diepe kopieën).

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

Merk op dat deepcopyeen memoisatiewoordenboek van id(original)(of identiteitsnummers) bijhoudt aan kopieën. Om te genieten van goed gedrag met recursieve gegevensstructuren, zorg er dan voor dat u nog niet een kopie hebt gedaan en als u dat hebt, retourneert u dat.

Dus laten we een object maken:

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

en copymaakt een ondiepe kopie:

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

en deepcopyNu maakt een diepe kopie:

>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]

Antwoord 3, Autoriteit 8%

Ondiepe kopie met 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]

Diepe kopie met 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]

Documentatie: https://docs.python.org/3/library/copy. html

Getest op Python 3.6.5.


Antwoord 4

Ik geloof dat het volgende zou moeten werken met veel goed opgevoede klassen in Python:

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

(Natuurlijk heb ik het hier niet over “diepe kopieën”, wat een ander verhaal is, en wat misschien niet een heel duidelijk concept is — hoe diep is diep genoeg?)

Volgens mijn tests met Python 3, retourneert het voor onveranderlijke objecten, zoals tuples of strings, hetzelfde object (omdat het niet nodig is om een ondiepe kopie van een onveranderlijk object te maken), maar voor lijsten of woordenboeken creëert het een onafhankelijke ondiepe kopie.

Natuurlijk werkt deze methode alleen voor klassen waarvan de constructeurs zich overeenkomstig gedragen. Mogelijke use-cases: een ondiepe kopie maken van een standaard Python-containerklasse.

Other episodes