Python'da bir nesnenin kopyasını nasıl oluşturabilirim?


200

Bir nesnenin kopyasını oluşturmak istiyorum. Yeni nesnenin eski nesnenin (alanların değerleri) tüm özelliklerine sahip olmasını istiyorum. Ama bağımsız nesneler istiyorum. Bu nedenle, yeni nesnenin alanlarının değerlerini değiştirirsem, eski nesnenin bundan etkilenmemesi gerekir.

Yanıtlar:


180

Bir nesnenin tamamen bağımsız bir kopyasını almak için copy.deepcopy()işlevi kullanabilirsiniz .

Sığ ve derin kopyalama hakkında daha fazla bilgi için lütfen bu sorunun diğer cevaplarına ve bu sorunun ilgili soruya vereceği güzel açıklamaya bakın .


2
Bu yanıt "Yanıt değil" olarak işaretlendi, silindi ve silindi - meta tartışma burada: meta.stackoverflow.com/questions/377844/…
Aaron Hall

@AaronHall Bana bildirdiğiniz için teşekkürler! Bu kesinlikle yazdığım en büyük cevap değil, ama zorla silinmemesi kararına katılıyorum. Biraz fırçalayacağım, ama tüm detaylarla (özellikle sizin) zaten cevaplar olduğu için, kısa tutacağım.
Sven Marnach

70

Python'da bir nesnenin kopyasını nasıl oluşturabilirim?

Bu nedenle, yeni nesnenin alanlarının değerlerini değiştirirsem, eski nesnenin bundan etkilenmemesi gerekir.

Değişebilir bir nesneyi kastediyorsun.

Python 3'te listeler bir copyyöntem alır (2'de kopya oluşturmak için bir dilim kullanırsınız):

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

Sığ Kopyalar

Sığ kopyalar yalnızca en dıştaki kabın kopyalarıdır.

list.copy sığ bir kopyadır:

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

İç nesnelerin bir kopyasını alamazsınız. Bunlar aynı nesnedir - bu yüzden mutasyona uğradığında, değişiklik her iki kapta da görülür.

Derin kopyalar

Derin kopyalar, her bir iç nesnenin özyinelemeli kopyalarıdır.

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

Değişiklikler orijinal belgeye değil, yalnızca kopyaya yansıtılır.

Değişmez nesneler

Değişmez nesnelerin genellikle kopyalanması gerekmez. Aslında, eğer denerseniz, Python size sadece orijinal nesneyi verecektir:

>>> 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'ın bir kopyalama yöntemi bile yok, bu yüzden bir dilim ile deneyelim:

>>> tuple_copy_attempt = a_tuple[:]

Ama aynı nesne olduğunu görüyoruz:

>>> tuple_copy_attempt is a_tuple
True

Benzer şekilde dizeler için:

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

ve frozensets için, bir copyyöntemi olmasına rağmen :

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

Değişmez nesneler ne zaman kopyalanır

Değişken olmayan objeler olmalı Kopyalanan bir değişken iç nesne gerekiyorsa kopyalanabilir.

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

Gördüğümüz gibi, kopyanın iç nesnesi mutasyona uğradığında, orijinal değil değiştirin.

Özel Nesneler

Özel nesneler genellikle verileri bir __dict__öznitelikte veya__slots__ (grup benzeri bir bellek yapısı) .

Kopyalanabilir bir nesne yapmak için __copy__(sığ kopyalar için) ve / veya __deepcopy__(derin kopyalar için ) tanımlayı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

deepcopyİle ilgili bir not sözlüğü tutarid(original) (veya kimlik numaralarının) not kopyalara . Özyinelemeli veri yapılarıyla iyi davranışların tadını çıkarmak için, daha önce bir kopya oluşturmadığınızdan ve varsa iade edin.

Öyleyse bir nesne yapalım:

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

Ve copysığ bir kopya yapar:

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

Ve deepcopyşimdi derin bir kopya çıkarıyor:

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

11

İle sığ kopya 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]

İle derin kopya 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]

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

Python 3.6.5 üzerinde test edilmiştir.


-2

Aşağıdakilerin Python'da iyi niyetli birçok sınıfla çalışması gerektiğine inanıyorum:

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

(Tabii ki, burada farklı bir hikaye olan ve çok açık bir kavram olmayan "derin kopyalar" dan bahsetmiyorum - yeterince derin ne kadar derin?)

Python 3 ile yaptığım testlere göre, tuples veya dizeler gibi değişmez nesneler için aynı nesneyi döndürür (çünkü değişmez bir nesnenin sığ bir kopyasını oluşturmaya gerek yoktur), ancak listeler veya sözlükler için bağımsız bir sığ kopya oluşturur .

Tabii ki bu yöntem sadece yapıcıları buna göre davranan sınıflar için işe yarar. Olası kullanım örnekleri: standart bir Python konteyner sınıfının sığ bir kopyasını oluşturma.


Bu temiz ve her şeydir, ancak kopyalama işleviniz özel sınıflar için başarısız olduğundan ve soru nesnelerle ilgili olduğu için soruyu cevaplamaz .
Jared Smith

@JaredSmith, sorunun tüm nesnelerle ilgili olduğu belirtilmedi . Derin veya sığ bir kopya hakkında bile net değildi (her zamanki sığ olanı varsayarım, ancak kabul edilen cevap derindir). Özel sınıflara gelince, onlar sizin ise, yöntemlerinde bu tür bir sözleşmeye saygı duyabilirsiniz __init__. Yani, bu yöntemin belirli amaçlar için yeterince iyi olabileceğini düşündüm. Her durumda, bu öneri hakkında bilgilendirici yorumlarla ilgileneceğim.
Alexey

class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = argBasic'i olduğu gibi düşünün . Ben yoksa foo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>senin o Anlamı copyfonksiyon sınıflarının bile en temel için kırılır. Yine, bu temiz bir hile (dolayısıyla DV yok), ama bir cevap değil.
Jared Smith

@JaredSmith, copy.copysığ kopyalar yapmak için bir yöntem olduğunu gördüm , ama belki de naif olarak, bana bir "sığ kopya oluşturucu" sağlamak sınıfın sorumluluğu olmalı gibi görünüyor. Böyle bir durumda neden aynı arayüz arayüzünü dictve listyapmıyorsunuz? Eğer sınıfınız nesnelerini kopyalama sorumluluğunu almak istiyorsa, neden bir if isinstance(arg, type(self))madde eklemeyesiniz __init__?
Alexey

1
Çünkü kullandığınız sınıfları tanımladığınız şekilde kontrol edemezsiniz. Bunlar, sadece bir örnek olarak, Python bağlarına sahip C programları olabilir (örn. GTK, openalpr, çekirdek kısımları). Bir üçüncü taraf kütüphanesi alıp her sınıfa kopyalama yöntemleri ekleseniz bile, bunu bağımlılık yönetiminize nasıl bağlayacağınızdan bahsetmiyoruz bile?
Jared Smith
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.