Lista zmienia się nieoczekiwanie po przypisaniu. Jak sklonować lub skopiować, aby temu zapobiec?

Podczas używania new_list = my_list, wszelkie modyfikacje new_list zmieniają my_list za każdym razem. Dlaczego tak jest i jak Mogę sklonować lub skopiować listę, aby temu zapobiec?

Author: John Kugelman, 2010-04-10

22 answers

Z new_list = my_list, tak naprawdę nie masz dwóch list. Przypisanie po prostu kopiuje odniesienie do listy, a nie rzeczywistej listy, więc zarówno new_list, jak i my_list odnoszą się do tej samej listy po przypisaniu.

Aby skopiować listę, masz różne możliwości:

  • Możesz użyć wbudowanego list.copy() metoda (dostępna od Pythona 3.3):

    new_list = old_list.copy()
    
  • Możesz go pokroić:

    new_list = old_list[:]
    

    Opinia Alexa Martelli (przynajmniej w 2007 ) chodzi o to, że jest to dziwna składnia i nie ma sensu jej używać kiedykolwiek . ;) (Jego zdaniem następny jest bardziej czytelny).

  • Możesz użyć wbudowanego list() funkcja:

    new_list = list(old_list)
    
  • Możesz użyć generic copy.copy():

    import copy
    new_list = copy.copy(old_list)
    

    Jest to trochę wolniejsze niż list(), ponieważ najpierw musi poznać typ danych old_list.

  • Jeśli lista zawiera obiekty i chcesz je również skopiować, użyj generic copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)
    
    Oczywiście najwolniejsza i najbardziej wymagająca pamięci metoda, ale czasami nieunikniona.

Przykład:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return 'Foo({!r})'.format(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance 
a.append('baz')
foo.val = 5

print('original: %r\nlist.copy(): %r\nslice: %r\nlist(): %r\ncopy: %r\ndeepcopy: %r'
      % (a, b, c, d, e, f))

Wynik:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
 3555
Author: Felix Kling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-09 01:52:34

Felix już dostarczył doskonałą odpowiedź, ale pomyślałem, że zrobię porównanie prędkości różnych metod:

  1. 10.59 sec (105.9 us / itn) - copy.deepcopy(old_list)
  2. 10.16 sec (101.6 us/itn) - czysty python Copy() metoda kopiowania klas za pomocą deepcopy
  3. [[17]} 1.488 sec (14.88 us/itn) - czysta metoda Pythona Copy() nie kopiuje klas (tylko dicty/listy/krotki)
  4. 0.325 sec (3.25 us/itn) - for item in old_list: new_list.append(item)
  5. 0.217 sec (2.17 us/itn) - [i for i in old_list] (a lista zrozumienie )
  6. 0.186 sec (1.86 us / itn) - copy.copy(old_list)
  7. 0.075 sec (0.75 us/itn) - list(old_list)
  8. 0.053 sec (0.53 us/itn) - new_list = []; new_list.extend(old_list)
  9. 0.039 sek (0.39 us / itn) - old_list[:] (lista krojenia )

Więc najszybsze jest krojenie listy. Ale należy pamiętać, że copy.copy(), list[:] i list(list), W przeciwieństwie do copy.deepcopy() i wersji Pythona nie kopiują żadnych list, słowników I instancji klas na liście, więc jeśli oryginały się zmienią, zmienią się w skopiowanym lista też i vice versa.

(Oto scenariusz, jeśli ktoś jest zainteresowany lub chce poruszyć jakieś problemy:)

from copy import deepcopy

class old_class:
    def __init__(self):
        self.blah = 'blah'

class new_class(object):
    def __init__(self):
        self.blah = 'blah'

dignore = {str: None, unicode: None, int: None, type(None): None}

def Copy(obj, use_deepcopy=True):
    t = type(obj)

    if t in (list, tuple):
        if t == tuple:
            # Convert to a list if a tuple to 
            # allow assigning to when copying
            is_tuple = True
            obj = list(obj)
        else: 
            # Otherwise just do a quick slice copy
            obj = obj[:]
            is_tuple = False

        # Copy each item recursively
        for x in xrange(len(obj)):
            if type(obj[x]) in dignore:
                continue
            obj[x] = Copy(obj[x], use_deepcopy)

        if is_tuple: 
            # Convert back into a tuple again
            obj = tuple(obj)

    elif t == dict: 
        # Use the fast shallow dict copy() method and copy any 
        # values which aren't immutable (like lists, dicts etc)
        obj = obj.copy()
        for k in obj:
            if type(obj[k]) in dignore:
                continue
            obj[k] = Copy(obj[k], use_deepcopy)

    elif t in dignore: 
        # Numeric or string/unicode? 
        # It's immutable, so ignore it!
        pass 

    elif use_deepcopy: 
        obj = deepcopy(obj)
    return obj

if __name__ == '__main__':
    import copy
    from time import time

    num_times = 100000
    L = [None, 'blah', 1, 543.4532, 
         ['foo'], ('bar',), {'blah': 'blah'},
         old_class(), new_class()]

    t = time()
    for i in xrange(num_times):
        Copy(L)
    print 'Custom Copy:', time()-t

    t = time()
    for i in xrange(num_times):
        Copy(L, use_deepcopy=False)
    print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t

    t = time()
    for i in xrange(num_times):
        copy.copy(L)
    print 'copy.copy:', time()-t

    t = time()
    for i in xrange(num_times):
        copy.deepcopy(L)
    print 'copy.deepcopy:', time()-t

    t = time()
    for i in xrange(num_times):
        L[:]
    print 'list slicing [:]:', time()-t

    t = time()
    for i in xrange(num_times):
        list(L)
    print 'list(L):', time()-t

    t = time()
    for i in xrange(num_times):
        [i for i in L]
    print 'list expression(L):', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(L)
    print 'list extend:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        for y in L:
            a.append(y)
    print 'list append:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(i for i in L)
    print 'generator expression extend:', time()-t
 654
Author: cryo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-12 14:16:17

Powiedziano miże Python 3.3 + dodaje list.copy() metoda, która powinna być tak szybka jak krojenie:

newlist = old_list.copy()

 165
Author: anatoly techtonik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 01:13:42

Jakie są opcje klonowania lub kopiowania listy w Pythonie?

W Pythonie 3 płytka Kopia może być wykonana za pomocą:

a_copy = a_list.copy()

W Pythonie 2 i 3 można uzyskać płytką kopię z pełnym fragmentem oryginału:

a_copy = a_list[:]

Wyjaśnienie

Istnieją dwa semantyczne sposoby kopiowania listy. Płytka Kopia tworzy nową listę tych samych obiektów, a głęboka Kopia tworzy nową listę zawierającą nowe równoważne obiekty.

Shallow list copy

A shallow copy kopiuje tylko samą listę, która jest kontenerem odniesień do obiektów na liście. Jeśli obiekty same w sobie są mutowalne i jeden z nich zostanie zmieniony, zmiana zostanie odzwierciedlona w obu listach.

Istnieją różne sposoby, aby to zrobić w Pythonie 2 i 3. Sposoby Pythona 2 będą również działać w Pythonie 3.

Python 2

W Pythonie 2 idiomatyczny sposób tworzenia płytkiej kopii listy jest z kompletnym fragmentem oryginału:
a_copy = a_list[:]

Ty może również wykonać to samo, przekazując listę przez konstruktor listy,

a_copy = list(a_list)

Ale użycie konstruktora jest mniej efektywne:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

W Pythonie 3 listy otrzymują metodę list.copy:

a_copy = a_list.copy()

W Pythonie 3.5:

>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125

Robienie kolejnego wskaźnika robi a nie robi kopię

Użycie new_list = my_list powoduje modyfikację new_list za każdym razem, gdy zmieni się my_list. Dlaczego?

my_list na tylko nazwa, która wskazuje na aktualną listę w pamięci. Kiedy mówisz new_list = my_list, że nie robisz kopii, po prostu dodajesz inną nazwę, która wskazuje na oryginalną listę w pamięci. Możemy mieć podobne problemy, gdy robimy kopie list.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

Lista jest tylko tablicą wskaźników do zawartości, więc płytka Kopia po prostu kopiuje wskaźniki, a więc masz dwie różne listy, ale mają tę samą zawartość. Aby wykonać kopie zawartości, potrzebujesz głębokiej kopii.

Deep kopie

Aby utworzyć głęboką kopię listy, w Pythonie 2 lub 3, Użyj deepcopy w module copy :

import copy
a_deep_copy = copy.deepcopy(a_list)

Aby zademonstrować, w jaki sposób pozwala nam to tworzyć nowe podlisty:

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

Widzimy więc, że głęboko skopiowana lista jest zupełnie inną listą niż oryginalna. Możesz zwijać swoją własną funkcję-ale nie rób tego. prawdopodobnie stworzysz błędy, których w przeciwnym razie nie miałbyś, używając standardowej funkcji deepcopy w bibliotece.

Nie używaj eval

Możesz to zobaczyć jako sposób na deepcopy, ale nie rób tego:]}
problematic_deep_copy = eval(repr(a_list))
    To niebezpieczne, szczególnie jeśli oceniasz coś ze źródła, któremu nie ufasz.
  1. to nie jest wiarygodne, jeśli podelement, który kopiujesz, nie ma reprezentacji, która może być ewalowana w celu odtworzenia równoważnego elementu.
  2. Jest też mniej wydajny.

W 64-bitowym Pythonie 2.7:

>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206

NA 64-bitowym Pythonie 3.5:

>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
 133
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-09 15:33:11

Istnieje już wiele odpowiedzi, które mówią, jak zrobić właściwą kopię, ale żadna z nich nie mówi, dlaczego oryginalna 'kopia' nie powiodła się.

Python nie przechowuje wartości w zmiennych; wiąże nazwy z obiektami. Twoje pierwotne zadanie zabrało obiekt, o którym mowa przez my_list i powiązało go z new_list. Bez względu na to, której nazwy użyjesz, nadal jest tylko jedna lista, więc zmiany wprowadzone podczas odwoływania się do niej jako my_list będą trwać przy odwoływaniu się do niej jako new_list. Każda z pozostałych odpowiedzi na to pytanie daje różne sposoby tworzenia nowego obiektu do powiązania new_list.

Każdy element listy działa jak nazwa, ponieważ każdy element wiąże się nie-wyłącznie z obiektem. Płytka Kopia tworzy nową listę, której elementy wiążą się z tymi samymi obiektami, co poprzednio.

new_list = list(my_list)  # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]

Aby posunąć Listę o krok dalej, skopiuj każdy obiekt, do którego odnosi się lista i powiązaj te kopie z nową listą.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

Nie jest to jeszcze głęboka Kopia, ponieważ każdy element listy może odnosić się do innych obiektów, podobnie jak lista jest związana z jej elementami. Aby rekurencyjnie skopiować każdy element na liście, a następnie każdy inny obiekt, do którego odnosi się każdy element, i tak dalej: wykonaj głęboką kopię.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

Patrz dokumentacja aby uzyskać więcej informacji na temat przypadków narożnych w kopiowaniu.

 58
Author: jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-05 16:01:12

Zacznijmy od początku i zbadajmy to pytanie.

Więc załóżmy, że masz dwie listy:

list_1=['01','98']
list_2=[['01','98']]

I musimy skopiować obie listy, teraz zaczynając od pierwszej listy:

Więc najpierw spróbujmy ustawić zmienną copy na naszą oryginalną listę, list_1:

copy=list_1

Jeśli myślisz, że Kopiuj skopiował listę_1, to się mylisz. Funkcja id może nam pokazać, czy dwie zmienne mogą wskazywać na ten sam obiekt. Spróbujmy to:

print(id(copy))
print(id(list_1))

Wyjście To:

4329485320
4329485320

Obie zmienne są dokładnie tym samym argumentem. Jesteś zaskoczony?

Ponieważ wiemy, że python nie przechowuje niczego w zmiennej, zmienne odnoszą się tylko do obiektu, a obiekt przechowuje wartość. Tutaj obiekt jest list, ale stworzyliśmy dwa odwołania do tego samego obiektu przez dwie różne nazwy zmiennych. Oznacza to, że obie zmienne wskazują na ten sam obiekt, tylko o różnych nazwach.

Kiedy copy=list_1, faktycznie robi:

Tutaj wpisz opis obrazka

Tutaj na liście obrazka_1 i copy są dwie nazwy zmiennych, ale obiekt jest taki sam dla obu zmiennych, które są list

Więc jeśli spróbujesz zmodyfikować skopiowaną listę, to zmieni ona również oryginalną listę, ponieważ lista jest tylko jedna, zmodyfikujesz tę listę bez względu na to, czy zrobisz to z skopiowanej listy, czy z oryginalnej listy:

copy[0]="modify"

print(copy)
print(list_1)

Wyjście:

['modify', '98']
['modify', '98']

Więc zmodyfikował oryginalną listę :

Przejdźmy teraz do metody pythonicznej do kopiowania list.

copy_1=list_1[:]

Ta metoda rozwiązuje pierwszy problem, który mieliśmy:

print(id(copy_1))
print(id(list_1))

4338792136
4338791432

Widzimy więc, że obie listy mają różne id i oznacza to, że obie zmienne wskazują na różne obiekty. Więc to, co się tutaj dzieje, to:

Tutaj wpisz opis obrazka

Teraz spróbujmy zmodyfikować listę i zobaczmy, czy nadal mamy do czynienia z poprzednim problemem:]}
copy_1[0]="modify"

print(list_1)
print(copy_1)

Wyjście To:

['01', '98']
['modify', '98']

Jako widać, że tylko zmodyfikował skopiowaną listę. To znaczy, że zadziałało.

Myślisz, że skończyliśmy? Nie. Spróbujmy skopiować naszą zagnieżdżoną listę.
copy_2=list_2[:]

list_2 powinna odnosić się do innego obiektu, który jest kopią list_2. Sprawdźmy:

print(id((list_2)),id(copy_2))

Otrzymujemy wyjście:

4330403592 4330403528

Teraz możemy założyć, że obie listy wskazują inny obiekt, więc spróbujmy go zmodyfikować i zobaczmy, czy daje to, co chcemy:

copy_2[0][1]="modify"

print(list_2,copy_2)

To daje nam wyjście:

[['01', 'modify']] [['01', 'modify']]

Może to wydawać się trochę mylące, ponieważ ta sama metoda, której wcześniej używaliśmy, działała. Spróbujmy to zrozumieć.

Kiedy robisz:

copy_2=list_2[:]
Kopiujesz tylko zewnętrzną listę, Nie wewnętrzną. Możemy jeszcze raz użyć funkcji id, aby to sprawdzić.
print(id(copy_2[0]))
print(id(list_2[0]))

Wyjście To:

4329485832
4329485832

Kiedy robimy copy_2=list_2[:], dzieje się tak:

Tutaj wpisz opis obrazka

Tworzy kopię listy, ale tylko zewnętrzną listę Kopiuj, nie zagnieżdżoną listę Kopiuj, zagnieżdżona lista jest taka sama dla obu zmiennych, więc jeśli spróbujesz zmodyfikować zagnieżdżoną listę, to zmieni ona również oryginalną listę, ponieważ obiekt zagnieżdżonej listy jest taki sam dla obu list.

Jakie jest rozwiązanie? Rozwiązaniem jest funkcja deepcopy.

from copy import deepcopy
deep=deepcopy(list_2)

Sprawdźmy to:

print(id((list_2)),id(deep))

4322146056 4322148040

Obie zewnętrzne listy mają różne identyfikatory, spróbujmy tego na wewnętrznych listach zagnieżdżonych.

print(id(deep[0]))
print(id(list_2[0]))

Wyjście To:

4322145992
4322145800

Jak widać oba identyfikatory są different, co oznacza, że obie zagnieżdżone listy wskazują teraz inny obiekt.

Oznacza to, kiedy robisz deep=deepcopy(list_2) co się naprawdę dzieje:

Tutaj wpisz opis obrazka

Obie zagnieżdżone listy wskazują inny obiekt i mają teraz oddzielną kopię zagnieżdżonej listy.

Teraz spróbujmy zmodyfikować zagnieżdżoną listę i sprawdzić, czy rozwiązała poprzedni problem, czy nie:]}
deep[0][1]="modify"
print(list_2,deep)

Wychodzi:

[['01', '98']] [['01', 'modify']]

Jak widzisz, nie zmodyfikował oryginalna zagnieżdżona lista, tylko zmodyfikowała skopiowaną listę.

 49
Author: Aaditya Ura,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-05 22:05:02

Użyj thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
 39
Author: Paul Tarjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-10 08:53:06

Python 3.6 Timings

Oto wyniki pomiaru czasu za pomocą Pythona 3.6.8. Należy pamiętać, że te czasy są względem siebie, a nie absolutne.

Trzymałem się tylko płytkich kopii, a także dodałem kilka nowych metod, które nie były możliwe w Python2, takich jak list.copy() (odpowiednik python3 slice ) i dwie formy rozpakowywania listy (*new_list, = list oraz new_list = [*list]):

METHOD                  TIME TAKEN
b = [*a]                2.75180600000021
b = a * 1               3.50215399999990
b = a[:]                3.78278899999986  # Python2 winner (see above)
b = a.copy()            4.20556500000020  # Python3 "slice equivalent" (see above)
b = []; b.extend(a)     4.68069800000012
b = a[0:len(a)]         6.84498999999959
*b, = a                 7.54031799999984
b = list(a)             7.75815899999997
b = [i for i in a]      18.4886440000000
b = copy.copy(a)        18.8254879999999
b = []
for item in a:
  b.append(item)        35.4729199999997

Widzimy, że zwycięzca Python2 nadal radzi sobie dobrze, ale nie przewyższa Python3 list.copy() przez dużo, zwłaszcza biorąc pod uwagę doskonałą czytelność tego ostatniego.

Ciemny koń jest metodą rozpakowywania i przepakowywania (b = [*a]), która jest ~25% szybsza niż surowe krojenie i ponad dwa razy szybsza niż druga metoda rozpakowywania (*b, = a).

b = a * 1 również robi zaskakująco dobrze.

Zauważ, że te metody Nie generują równoważne wyniki dla dowolnych danych wejściowych innych niż listy. wszystkie działają dla obiektów nadających się do cięcia, kilka działa dla dowolnych iterowalnych, ale tylko copy.copy() działa dla bardziej ogólnych obiektów Pythona.


Oto kod testowy dla zainteresowanych stron (szablon stąd):

import timeit

COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'

print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []; for item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a:  b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
 37
Author: River,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-14 02:54:00

Idiom Pythona do robienia tego to newList = oldList[:]

 34
Author: erisco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-10 08:53:19

Wszyscy inni współpracownicy Dali Świetne odpowiedzi, które działają, gdy masz pojedynczą listę (wyrównaną), jednak z metod wymienionych do tej pory, tylko copy.deepcopy() działa, aby klonować/kopiować listę i nie wskazywać na zagnieżdżone Obiekty list podczas pracy z wielowymiarowymi, zagnieżdżonymi listami (list of lists). Podczas gdy Felix Kling odnosi się do tego w swojej odpowiedzi, jest trochę więcej do problemu i ewentualnie obejście za pomocą wbudowanych, które mogą okazać się szybsze alternatywa dla deepcopy.

While new_list = old_list[:], copy.copy(old_list)' A Dla Py3k old_list.copy() działa dla list jednopoziomowych, powracają one do wskazywania obiektów list zagnieżdżonych w old_list i new_list, a zmiany w jednym z obiektów list są utrwalane w drugim.

Edit: New information bringed to light

Jak zauważył zarówno Aaron Hall jak i PM 2RING używanie eval() jest nie tylko złym pomysłem, jest również znacznie wolniejsze niż copy.deepcopy().

Oznacza to, że dla list wielowymiarowych jedyną opcją jest copy.deepcopy(). Mając to na uwadze, naprawdę nie jest to opcja, ponieważ wydajność idzie na południe, gdy próbujesz jej użyć na średniej wielkości tablicy wielowymiarowej. Próbowałem timeit użyć tablicy 42x42, nie niespotykanej ani nawet tak dużej do zastosowań bioinformatycznych, i zrezygnowałem z czekania na odpowiedź i zacząłem pisać moją edycję do tego posta.

Wydaje się, że jedyną realną opcją jest aby zainicjować wiele list i pracować nad nimi niezależnie. Jeśli ktoś ma jakieś inne sugestie, jak radzić sobie z wielowymiarowym kopiowaniem list, byłoby to mile widziane.

Jak stwierdzili inni, tam są istotne problemy z wydajnością przy użyciu modułu copy oraz copy.deepcopy dla list wielowymiarowych.

 20
Author: AMR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Dziwi mnie, że jeszcze o tym nie wspomniano, więc dla kompletności...

Możesz rozpakować listę za pomocą "operatora splat": *, który również skopiuje elementy Twojej listy.

old_list = [1, 2, 3]

new_list = [*old_list]

new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]

Oczywistym minusem tej metody jest to, że jest ona dostępna tylko w Pythonie 3.5+.

Biorąc pod uwagę czas, wydaje się to działać lepiej niż inne popularne metody.

x = [random.random() for _ in range(1000)]

%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]

%timeit a = [*x]

#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
 14
Author: SCB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-26 02:33:47

W udzielonych już odpowiedziach brakowało bardzo prostego podejścia, niezależnego od wersji Pythona, z którego można korzystać przez większość czasu (przynajmniej ja):

new_list = my_list * 1       #Solution 1 when you are not using nested lists

Jeśli jednak my_list zawiera inne kontenery (np. listy zagnieżdżone) musisz użyć deepcopy tak, jak inni sugerowali w odpowiedziach powyżej z biblioteki kopiowania. Na przykład:

import copy
new_list = copy.deepcopy(my_list)   #Solution 2 when you are using nested lists

.Bonus : Jeśli nie chcesz kopiować elementów użyj (aka shallow copy):

new_list = my_list[:]

Let ' s understand difference between Rozwiązanie # 1 i Rozwiązanie #2

>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55 
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])

Jak widać rozwiązanie # 1 działało idealnie, gdy nie korzystaliśmy z zagnieżdżonych list. Sprawdźmy, co się stanie, gdy zastosujemy rozwiązanie #1 do zagnieżdżonych list.

>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]   #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]       #Solution #2 - DeepCopy worked in nested list
 8
Author: jainashish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-01 08:08:25

Zauważ, że są przypadki, w których jeśli zdefiniowałeś własną klasę niestandardową i chcesz zachować atrybuty, powinieneś użyć copy.copy() lub copy.deepcopy() zamiast alternatyw, na przykład w Pythonie 3:

import copy

class MyList(list):
    pass

lst = MyList([1,2,3])

lst.name = 'custom list'

d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}


for k,v in d.items():
    print('lst: {}'.format(k), end=', ')
    try:
        name = v.name
    except AttributeError:
        name = 'NA'
    print('name: {}'.format(name))

Wyjścia:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
 8
Author: Chris_Rands,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-16 14:31:22
new_list = my_list[:]

new_list = my_list Spróbuj to zrozumieć. Załóżmy, że my_list znajduje się w pamięci sterty w lokalizacji X, tzn. my_list wskazuje na X. Teraz przypisując new_list = my_list pozwalasz new_list wskazywać na X. Jest to znane jako płytka Kopia.

Teraz, jeśli przypisujesz new_list = my_list[:], po prostu kopiujesz każdy obiekt my_list do new_list. Jest to znane jako głęboka Kopia.

W inny sposób można to zrobić są :

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)
 5
Author: Ravi Shankar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-26 21:03:30

Chciałem napisać coś nieco innego niż inne odpowiedzi. Chociaż najprawdopodobniej nie jest to najbardziej zrozumiała lub najszybsza opcja, zapewnia trochę wewnętrznego widoku na to, jak działa głęboka Kopia, a także jest inną alternatywną opcją głębokiego kopiowania. Nie ma znaczenia, czy moja funkcja ma błędy, ponieważ chodzi o to, aby pokazać sposób kopiowania obiektów, takich jak odpowiedzi na pytania, ale także aby użyć tego jako punktu do wyjaśnienia, jak deepcopy działa na swoim rdzeń.

Rdzeniem każdej funkcji głębokiego kopiowania jest sposób na płytką kopię. Jak? Proste. Każda funkcja głębokiego kopiowania duplikuje tylko kontenery obiektów niezmiennych. Podczas głębokiej kopiowania zagnieżdżonej listy duplikuje się tylko zewnętrzne listy, a nie zmienne obiekty wewnątrz list. Powielasz tylko kontenery. To samo działa również na zajęciach. Gdy wykonujesz deepcopy klasy, wykonujesz deepcopy wszystkich jej mutowalnych atrybutów. Więc jak? Dlaczego wystarczy skopiować kontenery?, jak listy, dicty, krotki, itery, klasy i instancje klas?

To proste. Mutowalny obiekt nie może być duplikowany. Nigdy nie można jej zmienić, więc jest to tylko jedna wartość. Oznacza to, że nigdy nie musisz powielać ciągów znaków, cyfr, boolis ani żadnego z nich. Ale jak powieliłbyś kontenery? Proste. Po prostu zainicjalizuj nowy kontener ze wszystkimi wartościami. Deepcopy opiera się na rekurencji. Duplikuje wszystkie pojemniki, nawet te z pojemnikami w nich, aż nie ma pojemników. Kontener jest obiektem niezmiennym.

Kiedy już to wiesz, całkowite powielenie obiektu bez żadnych odniesień jest dość łatwe. Oto funkcja do głębokiego kopiowania podstawowych typów danych (nie działa dla klas niestandardowych, ale zawsze możesz to dodać)

def deepcopy(x):
  immutables = (str, int, bool, float)
  mutables = (list, dict, tuple)
  if isinstance(x, immutables):
    return x
  elif isinstance(x, mutables):
    if isinstance(x, tuple):
      return tuple(deepcopy(list(x)))
    elif isinstance(x, list):
      return [deepcopy(y) for y in x]
    elif isinstance(x, dict):
      values = [deepcopy(y) for y in list(x.values())]
      keys = list(x.keys())
      return dict(zip(keys, values))

Własny, wbudowany deepcopy Pythona opiera się na tym przykładzie. Jedyną różnicą jest to, że obsługuje inne typy, a także obsługuje klasy użytkowników przez duplikowanie atrybutów do nowej duplikowanej klasy i blokuje również infinite-recursion z odniesieniem do obiektu, który jest już widziany za pomocą listy notatek lub słownika. I to jest naprawdę to do robienia głębokich kopii. W istocie zrobienie głębokiej kopii to po prostu płytkie kopie. Mam nadzieję, że ta odpowiedź doda coś do pytania.

Przykłady

Powiedz, że masz tę listę: [1, 2, 3]. Niezmienne liczby nie mogą być powielane, ale druga warstwa może. Można go powielać używając listy: [x dla X w [1, 2, 3]

Wyobraź sobie, że masz tę listę.: [[1, 2], [3, 4], [5, 6]]. Tym razem chcemy utworzyć funkcję, która używa rekurencji do głębokiego kopiowania wszystkich warstw listy. Zamiast poprzedniej listy:
[x for x in _list]

Używa nowego dla list:

[deepcopy_list(x) for x in _list]

I deepcopy_list wygląda tak:

def deepcopy_list(x):
  if isinstance(x, (str, bool, float, int)):
    return x
  else:
    return [deepcopy_list(y) for y in x]

Teraz masz funkcję, która może głęboko skopiować dowolną listę strs, bools, floast, ints a nawet list do nieskończenie wiele warstw przy użyciu rekurencji. No i proszę, Głębokie kopiowanie.

TLDR : Deepcopy używa rekurencji do powielania obiektów i zwraca tylko te same niezmienne obiekty co poprzednio, ponieważ niezmienne obiekty nie mogą być powielane. Jednakże, głęboko kopiuje najbardziej wewnętrzne warstwy mutowalnych obiektów, aż dotrze do zewnętrznej mutowalnej warstwy obiektu.

 3
Author: Corman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-08 02:25:09

Lekka praktyczna perspektywa do spojrzenia w pamięć poprzez id i gc.

>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']

>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272) 
     |           |
      -----------

>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
     |           |           |
      -----------------------

>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
     |           |           |
      -----------------------

>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word'])  # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
     |           |
      -----------

>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
     |           |           |
      -----------------------

>>> import gc
>>> gc.get_referrers(a[0]) 
[['hell', 'word'], ['hell', 'word']]  # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None) 
 3
Author: B.Mr.W.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-23 19:01:46

Pamiętaj, że w Pythonie kiedy to robisz:

    list1 = ['apples','bananas','pineapples']
    list2 = list1

List2 nie przechowuje rzeczywistej listy, ale odniesienie do list1. Więc kiedy robisz cokolwiek list1, list2 również się zmienia. użyj modułu Kopiuj (nie domyślnie, Pobierz na pip), aby wykonać oryginalną kopię listy (copy.copy() dla prostych list, copy.deepcopy() dla zagnieżdżonych). To sprawia, że kopia nie zmienia się wraz z pierwszą listą.

 3
Author: Dr. Hippo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-22 12:44:40

Opcja deepcopy jest jedyną metodą, która działa dla mnie:

from copy import deepcopy

a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')

Prowadzi do wyjścia:

Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
 1
Author: shahar_m,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-11 11:19:40

Dzieje się tak dlatego, że linia new_list = my_list przypisuje nowe odniesienie do zmiennej my_list, która jest new_list Jest to podobne do kodu C podanego poniżej,

int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;

Powinieneś użyć modułu Kopiuj, aby utworzyć nową listę przez

import copy
new_list = copy.deepcopy(my_list)
 1
Author: Roshin Raphel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-04 10:40:28

Sposób użycia zależy od zawartości kopiowanej listy. Jeśli lista zawiera zagnieżdżone dicts, to deepcopy jest jedyną metodą, która działa, w przeciwnym razie większość metod wymienionych w odpowiedziach (slice, loop [for], copy, extend, combine lub unpack) będzie działać i działać w podobnym czasie (z wyjątkiem loop i deepcopy, które wstępnie uformowały najgorsze).

Skrypt

from random import randint
from time import time
import copy

item_count = 100000

def copy_type(l1: list, l2: list):
  if l1 == l2:
    return 'shallow'
  return 'deep'

def run_time(start, end):
  run = end - start
  return int(run * 1000000)

def list_combine(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = [] + l1
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'combine', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_extend(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = []
  l2.extend(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'extend', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_unpack(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = [*l1]
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'unpack', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_deepcopy(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = copy.deepcopy(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'deepcopy', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_copy(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = list.copy(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'copy', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_slice(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = l1[:]
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'slice', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_loop(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = []
  for i in range(len(l1)):
    l2.append(l1[i])
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'loop', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

def list_list(data):
  l1 = [data for i in range(item_count)]
  start = time()
  l2 = list(l1)
  end = time()
  if type(data) == dict:
    l2[0]['test'].append(1)
  elif type(data) == list:
    l2.append(1)
  return {'method': 'list()', 'copy_type': copy_type(l1, l2), 
          'time_µs': run_time(start, end)}

if __name__ == '__main__':
  list_type = [{'list[dict]': {'test': [1, 1]}}, 
          {'list[list]': [1, 1]}]
  store = []
  for data in list_type:
    key = list(data.keys())[0]
    store.append({key: [list_unpack(data[key]), list_extend(data[key]), 
                list_combine(data[key]), list_deepcopy(data[key]), 
                list_copy(data[key]), list_slice(data[key]),           
                list_loop(data[key])]})
  print(store)

Wyniki

[{"list[dict]": [
  {"method": "unpack", "copy_type": "shallow", "time_µs": 56149},
  {"method": "extend", "copy_type": "shallow", "time_µs": 52991},
  {"method": "combine", "copy_type": "shallow", "time_µs": 53726},
  {"method": "deepcopy", "copy_type": "deep", "time_µs": 2702616},
  {"method": "copy", "copy_type": "shallow", "time_µs": 52204},
  {"method": "slice", "copy_type": "shallow", "time_µs": 52223},
  {"method": "loop", "copy_type": "shallow", "time_µs": 836928}]},
{"list[list]": [
  {"method": "unpack", "copy_type": "deep", "time_µs": 52313},
  {"method": "extend", "copy_type": "deep", "time_µs": 52550},
  {"method": "combine", "copy_type": "deep", "time_µs": 53203},
  {"method": "deepcopy", "copy_type": "deep", "time_µs": 2608560},
  {"method": "copy", "copy_type": "deep", "time_µs": 53210},
  {"method": "slice", "copy_type": "deep", "time_µs": 52937},
  {"method": "loop", "copy_type": "deep", "time_µs": 834774}
]}]
 1
Author: fjemi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-30 20:17:42

Jest na to prosta technika.

Kod:

number=[1,2,3,4,5,6] #Original list
another=[] #another empty list
for a in number: #here I am declaring variable (a) as an item in the list (number)
    another.append(a) #here we are adding the items of list (number) to list (another)
print(another)

Wyjście:

>>> [1,2,3,4,5,6]

Mam nadzieję, że było to przydatne dla Twojego zapytania.

 -1
Author: Code Carbonate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-03 03:38:24

Istnieje inny sposób na skopiowanie listy, która nie była na liście do tej pory: dodanie pustej listy: l2 = l + []. Testowałem go z Pythonem 3.8:

l = [1,2,3]
l2 = l + []
print(l,l2)
l[0] = 'a'
print(l,l2)
Nie najlepsza odpowiedź, ale działa.
 -1
Author: Laurent Lyaudet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-03 13:15:57