Saya memahami perbedaan antara copy
vs. deepcopy
dalam modul salin. Saya telah menggunakan copy.copy
dan copy.deepcopy
sebelumnya berhasil, tetapi ini adalah pertama kalinya saya benar-benar kelebihan beban metode __copy__
dan __deepcopy__
. Aku sudah Googled sekitar dan melihat melalui built-in modul Python untuk mencari contoh dari __copy__
dan __deepcopy__
fungsi (misalnya sets.py
, decimal.py
, dan fractions.py
), tapi aku masih belum 100% yakin aku punya itu benar.
Inilah skenario saya:
Saya memiliki objek konfigurasi. Awalnya, saya akan membuat contoh satu objek konfigurasi dengan satu set nilai default. Konfigurasi ini akan diserahkan ke beberapa objek lainnya (untuk memastikan semua objek dimulai dengan konfigurasi yang sama). Namun, begitu interaksi pengguna dimulai, setiap objek perlu menyesuaikan konfigurasinya secara independen tanpa mempengaruhi konfigurasi satu sama lain (yang mengatakan kepada saya bahwa saya perlu membuat salinan dalam dari konfigurasi awal saya untuk dibagikan).
Berikut contoh objeknya:
class ChartConfig(object):
def __init__(self):
#Drawing properties (Booleans/strings)
self.antialiased = None
self.plot_style = None
self.plot_title = None
self.autoscale = None
#X axis properties (strings/ints)
self.xaxis_title = None
self.xaxis_tick_rotation = None
self.xaxis_tick_align = None
#Y axis properties (strings/ints)
self.yaxis_title = None
self.yaxis_tick_rotation = None
self.yaxis_tick_align = None
#A list of non-primitive objects
self.trace_configs = []
def __copy__(self):
pass
def __deepcopy__(self, memo):
pass
Apa cara yang tepat untuk mengimplementasikan metode copy
dan deepcopy
pada objek ini untuk memastikan copy.copy
dan copy.deepcopy
memberi saya perilaku yang tepat?
sumber
Jawaban:
Rekomendasi untuk menyesuaikan ada di bagian paling akhir halaman dokumen :
Karena Anda tampaknya tidak peduli tentang kustomisasi pengawetan, mendefinisikan
__copy__
dan__deepcopy__
sepertinya cara yang tepat untuk Anda.Secara khusus,
__copy__
(salinan dangkal) cukup mudah dalam kasus Anda ...:__deepcopy__
akan serupa (menerimamemo
argumen juga) tetapi sebelum mengembalikannya harus memanggilself.foo = deepcopy(self.foo, memo)
atribut apa punself.foo
yang membutuhkan penyalinan mendalam (pada dasarnya atribut yang merupakan wadah - daftar, dicts, objek non-primitif yang menyimpan barang lain melalui__dict__
s mereka ).sumber
__copy__
/__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? Tidakkah kamu benar-benar bermaksudnewone.foo = ...
?__init__
. Bukan itu yang dilakukan salinan. Juga sangat sering terjadi kasus penggunaan di mana pengawetan dan penyalinan harus berbeda. Sebenarnya, saya bahkan tidak tahu mengapa copy mencoba menggunakan protokol pengawetan secara default. Menyalin adalah untuk manipulasi dalam memori, pengawetan adalah untuk persistensi lintas zaman; mereka adalah hal-hal yang sama sekali berbeda yang tidak memiliki banyak hubungan satu sama lain.Dengan menggabungkan jawaban Alex Martelli dan komentar Rob Young, Anda mendapatkan kode berikut:
cetakan
di sini
__deepcopy__
mengisimemo
dict untuk menghindari penyalinan berlebih jika objek itu sendiri direferensikan dari anggotanya.sumber
Transporter
?Transporter
adalah nama kelas yang saya tulis. Untuk kelas itu saya ingin menimpa perilaku deepcopy.Transporter
?__deepcopy__
harus menyertakan tes untuk menghindari rekursi tak terbatas: <! - language: lang-python -> d = id (self) result = memo.get (d, None) jika hasilnya bukan None: return resultmemo[id(self)]
sebenarnya digunakan untuk mencegah rekursi tak terbatas. Saya telah mengumpulkan contoh singkat yang menunjukkan bahwa secaracopy.deepcopy()
internal membatalkan panggilan ke suatu objek jika ituid()
adalah kuncinyamemo
, benar? Perlu juga dicatat bahwadeepcopy()
tampaknya melakukan ini sendiri secara default , yang membuatnya sulit untuk membayangkan kasus di mana mendefinisikan__deepcopy__
secara manual benar-benar diperlukan ...Mengikuti jawaban Peter yang sangat baik , untuk mengimplementasikan sebuah custom deepcopy, dengan sedikit perubahan pada implementasi default (misalnya hanya memodifikasi sebuah field seperti yang saya butuhkan):
sumber
delattr(self, '__deepcopy__')
kemudiansetattr(self, '__deepcopy__', deepcopy_method)
?Tidak jelas dari masalah Anda mengapa Anda perlu mengganti metode ini, karena Anda tidak ingin melakukan penyesuaian apa pun pada metode penyalinan.
Bagaimanapun, jika Anda ingin menyesuaikan salinan dalam (misalnya dengan berbagi beberapa atribut dan menyalin yang lain), berikut solusinya:
sumber
__deepcopy__
metode reset karena akan memiliki__deepcopy__
= Tidak ada?__deepcopy__
metode tidak ditemukan (atauobj.__deepcopy__
mengembalikan Tidak ada), makadeepcopy
kembali ke fungsi penyalinan dalam standar. Ini dapat dilihat di sini__deepcopy__=None
atribut palsu dari klon. Lihat kode baru.Saya mungkin sedikit kurang spesifik, tapi ini dia;
Dari
copy
dokumen ;Dengan kata lain:
copy()
akan menyalin hanya elemen atas dan meninggalkan sisanya sebagai penunjuk ke dalam struktur aslinya.deepcopy()
akan menyalin semuanya secara rekursif.Artinya,
deepcopy()
itulah yang Anda butuhkan.Jika Anda perlu melakukan sesuatu yang sangat spesifik, Anda dapat menggantinya
__copy__()
atau__deepcopy__()
, seperti yang dijelaskan di manual. Secara pribadi, saya mungkin akan mengimplementasikan fungsi biasa (misalnyaconfig.copy_config()
atau semacamnya) untuk membuatnya jelas bahwa itu bukan perilaku standar Python.sumber
__copy__(
) dan__deepcopy__()
. docs.python.org/library/copy.htmlThe
copy
modul menggunakan akhirnya__getstate__()
/ acar protokol , sehingga ini juga target yang valid untuk override.__setstate__()
Implementasi default hanya mengembalikan dan menyetel
__dict__
kelas, jadi Anda tidak perlu meneleponsuper()
dan mengkhawatirkan trik pintar Eino Gourdin di atas .sumber
Membangun jawaban bersih Antony Hatchkins, inilah versi saya di mana kelas yang dimaksud berasal dari kelas khusus lain (yang perlu kita panggil
super
):sumber