Rozumiem różnicę między copy
a deepcopy
w module kopiowania. Używałem copy.copy
i copy.deepcopy
wcześniej z powodzeniem, ale to jest pierwszy raz, kiedy faktycznie przeładowałem metody __copy__
i __deepcopy__
. Już Googled się i spojrzał przez wbudowany w modułach Pythona szukać wystąpień __copy__
i __deepcopy__
funkcji (np sets.py
, decimal.py
i fractions.py
), ale nadal nie jestem w 100% pewien, że mam rację.
Oto mój scenariusz:
Mam obiekt konfiguracyjny. Początkowo zamierzam utworzyć wystąpienie jednego obiektu konfiguracyjnego z domyślnym zestawem wartości. Ta konfiguracja zostanie przekazana do wielu innych obiektów (aby zapewnić, że wszystkie obiekty będą miały taką samą konfigurację). Jednak po rozpoczęciu interakcji z użytkownikiem każdy obiekt musi niezależnie dostosować swoje konfiguracje, nie wpływając na wzajemne konfiguracje (co mówi mi, że będę musiał wykonać głębokie kopie mojej początkowej konfiguracji).
Oto przykładowy obiekt:
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
Jaki jest właściwy sposób wdrożenia metod copy
i deepcopy
na tym obiekcie, aby zapewnić copy.copy
i copy.deepcopy
zapewnić mi właściwe zachowanie?
źródło
Odpowiedzi:
Zalecenia dotyczące dostosowywania znajdują się na samym końcu strony z dokumentami :
Ponieważ wydaje się, że nie zależy Ci na dostosowywaniu marynowania, zdefiniowanie
__copy__
i__deepcopy__
zdecydowanie wydaje się być właściwą drogą dla Ciebie.W szczególności
__copy__
(płytka kopia) jest w twoim przypadku dość łatwa ...:__deepcopy__
byłoby podobnie (akceptując równieżmemo
argument), ale przed powrotem musiałby wywołaćself.foo = deepcopy(self.foo, memo)
dowolny atrybut,self.foo
który wymaga głębokiego kopiowania (zasadniczo atrybuty, które są kontenerami - listy, dykty, nieprymitywne obiekty, które przechowują inne rzeczy przez swoje__dict__
).źródło
__copy__
/__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? Naprawdę nie masz na myślinewone.foo = ...
?__init__
. To nie jest to, co robi kopia. Ponadto bardzo często występuje przypadek użycia, w którym wytrawianie i kopiowanie muszą być inne. W rzeczywistości nawet nie wiem, dlaczego copy domyślnie próbuje używać protokołu wytrawiania. Kopiowanie służy do manipulacji w pamięci, wytrawianie służy do trwałości między epokami; są to zupełnie różne rzeczy, które mają ze sobą niewielki związek.Łącząc odpowiedź Alexa Martellego i komentarz Roba Younga, otrzymujemy następujący kod:
wydruki
tutaj
__deepcopy__
wypełniamemo
dict, aby uniknąć nadmiernego kopiowania w przypadku odniesienia do samego obiektu z jego elementu członkowskiego.źródło
Transporter
?Transporter
to nazwa mojej klasy, którą piszę. Dla tej klasy chcę zastąpić zachowanie Deepcopy.Transporter
?__deepcopy__
powinienem uwzględnić test, aby uniknąć nieskończonej rekurencji: <! - język: lang-python -> d = id (self) result = memo.get (d, None) jeśli wynik nie jest None: return resultmemo[id(self)]
faktycznie jest używany do zapobiegania nieskończonej rekurencji. Złożyłem krótki przykład, który sugeruje, żecopy.deepcopy()
wewnętrznie przerywa wywołanie obiektu, jeśliid()
jest to kluczmemo
, prawda? Warto również zauważyć, że domyślniedeepcopy()
wydaje się , że robi to samodzielnie, przez co trudno sobie wyobrazić przypadek, w którym__deepcopy__
ręczne definiowanie jest faktycznie potrzebne ...Podążając za doskonałą odpowiedzią Petera , aby zaimplementować niestandardową głęboką kopię, z minimalnymi zmianami w domyślnej implementacji (np. Po prostu zmodyfikuj pole, tak jak potrzebowałem):
źródło
delattr(self, '__deepcopy__')
wówczassetattr(self, '__deepcopy__', deepcopy_method)
?Z Twojego problemu nie wynika jasno, dlaczego musisz zastąpić te metody, ponieważ nie chcesz dostosowywać metod kopiowania.
W każdym razie, jeśli chcesz dostosować głęboką kopię (np. Udostępniając niektóre atrybuty i kopiując inne), oto rozwiązanie:
źródło
__deepcopy__
resetowania metody, ponieważ będzie miał__deepcopy__
= None?__deepcopy__
metoda nie zostanie znaleziona (lubobj.__deepcopy__
zwróci wartość None),deepcopy
wraca do standardowej funkcji głębokiego kopiowania. Można to zobaczyć tutaj__deepcopy__=None
atrybut z klona. Zobacz nowy kod.Mogę być trochę odbiegający od szczegółów, ale proszę bardzo;
Z
copy
dokumentów ;Innymi słowy:
copy()
skopiuje tylko górny element, a resztę pozostawi jako wskaźniki do oryginalnej struktury.deepcopy()
będzie rekurencyjnie kopiować wszystko.To znaczy,
deepcopy()
czego potrzebujesz.Jeśli chcesz zrobić coś naprawdę konkretnego, możesz zastąpić
__copy__()
lub__deepcopy__()
, zgodnie z opisem w instrukcji. Osobiście prawdopodobnie zaimplementowałbym zwykłą funkcję (np.config.copy_config()
Lub coś podobnego), aby wyjaśnić, że nie jest to standardowe zachowanie Pythona.źródło
__copy__(
) i__deepcopy__()
. docs.python.org/library/copy.htmlcopy
Moduł wykorzystuje ostatecznie__getstate__()
/ protokół staliBejcowanie , więc są one również ważne cele przesłonić.__setstate__()
Domyślna implementacja po prostu zwraca i ustawia
__dict__
klasę, więc nie musisz dzwonićsuper()
i martwić się sprytną sztuczką Eino Gourdina, powyżej .źródło
Opierając się na czystej odpowiedzi Antony'ego Hatchkinsa, oto moja wersja, w której dana klasa pochodzi z innej klasy niestandardowej (st, musimy zadzwonić
super
):źródło