Як глибоко скопіювати список?


150

У мене є проблема зі списком копії:

Тож після того, як я приїхав E0з 'get_edge', роблю копію E0, зателефонувавши 'E0_copy = list(E0)'. Тут я здогадуюсь E0_copyглибокої копії E0, і я переходжу E0_copyдо неї 'karger(E)'. Але в основній функції.
Чому результат 'print E0[1:10]'до циклу for не збігається з результатом після циклу for?

Нижче мій код:

def get_graph():
    f=open('kargerMinCut.txt')
    G={}
    for line in f:
        ints = [int(x) for x in line.split()]
        G[ints[0]]=ints[1:len(ints)]
    return G

def get_edge(G):
    E=[]
    for i in range(1,201):
        for v in G[i]:
            if v>i:
                E.append([i,v])
    print id(E)
    return E

def karger(E):
    import random
    count=200 
    while 1:
        if count == 2:
            break
        edge = random.randint(0,len(E)-1)
        v0=E[edge][0]
        v1=E[edge][1]                   
        E.pop(edge)
        if v0 != v1:
            count -= 1
            i=0
            while 1:
                if i == len(E):
                    break
                if E[i][0] == v1:
                    E[i][0] = v0
                if E[i][1] == v1:
                    E[i][1] = v0
                if E[i][0] == E[i][1]:
                    E.pop(i)
                    i-=1
                i+=1

    mincut=len(E)
    return mincut


if __name__=="__main__":
    import copy
    G = get_graph()
    results=[]
    E0 = get_edge(G)
    print E0[1:10]               ## this result is not equal to print2
    for k in range(1,5):
        E0_copy=list(E0)         ## I guess here E0_coypy is a deep copy of E0
        results.append(karger(E0_copy))
       #print "the result is %d" %min(results)
    print E0[1:10]               ## this is print2

2
Також b = a [:] є дрібною копією. Перегляньте stackoverflow.com/questions/16270374/…
Арвінд Харан,

Відповіді:


231

E0_copyне є глибокою копією. Ви не можете зробити глибоку копію, використовуючи list()(Обидві list(...)та testList[:]є дрібними копіями).

Ви використовуєте copy.deepcopy(...)для глибокого копіювання списку.

deepcopy(x, memo=None, _nil=[])
    Deep copy operation on arbitrary Python objects.

Дивіться такий фрагмент -

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b   # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]

Тепер дивіться deepcopyоперацію

>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b    # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]

3
Дякую. Але я подумав, що список () - це глибока копія, оскільки id (E0) не дорівнює id (E0_copy). Не могли б ви пояснити, чому це відбувається?
Шень

15
list (...) не робить рекурсивно копії внутрішніх об'єктів. Він робить лише копію самого зовнішнього списку, в той же час посилаючись на внутрішні списки з попередньої змінної, отже, коли ви мутуєте внутрішні списки, зміна відображається як у вихідному списку, так і в неглибокій копії.
Сукрит Калра

1
Ви можете побачити, що дрібне копіювання посилань на внутрішні списки, перевіривши, що id (a [0]) == id (b [0]), де b = list (a) і a - список списків.
Сукрит Калра

list1.append (list2) також є дрібною копією
списку2

60

Я вважаю, що багато програмістів зіткнулися з однією або двома проблемами інтерв'ю, коли їх просять скопіювати перелік пов'язаного списку, однак ця проблема важче, ніж це звучить!

у python є модуль під назвою "копіювати" з двома корисними функціями

import copy
copy.copy()
copy.deepcopy()

copy () - це функція дрібної копіювання, якщо даний аргумент є складною структурою даних, наприклад, списком , python створить інший об'єкт того ж типу (у цьому випадку новий список ), але для всього, що знаходиться всередині старого списку, копіюється лише їх посилання

# think of it like
newList = [elem for elem in oldlist]

Інтуїтивно можна вважати, що deepcopy () буде дотримуватися тієї ж парадигми, і різниця полягає лише в тому, що для кожного елема ми будемо рекурсивно називати deepcopy (так само, як відповідь mbcoder)

але це неправильно!

deepcopy () фактично зберігають графічну структуру вихідних складених даних:

a = [1,2]
b = [a,a] # there's only 1 object a
c = deepcopy(b)

# check the result
c[0] is a # return False, a new object a' is created
c[0] is c[1] # return True, c is [a',a'] not [a',a'']

це складна частина, під час процесу deepcopy () використовується хешбел (словник у python) для відображення: "ref_ old_object ref to new_object ref", це запобігає непотрібним копіям і, таким чином, збереже структуру скопійованих даних складених даних

офіційний док


18

Якщо вміст списку є примітивними типами даних, ви можете використовувати розуміння

new_list = [i for i in old_list]

Можна розмістити його для багатовимірних списків, таких як:

new_grid = [[i for i in row] for row in grid]

5

Якщо ваші list elementsє , immutable objectsто ви можете використовувати це, в іншому випадку ви повинні використовувати deepcopyз copyмодуля.

ви також можете скористатися найкоротшим способом для глибокого копіювання listподібного.

a = [0,1,2,3,4,5,6,7,8,9,10]
b = a[:] #deep copying the list a and assigning it to b
print id(a)
20983280
print id(b)
12967208

a[2] = 20
print a
[0, 1, 20, 3, 4, 5, 6, 7, 8, 9,10]
print b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10]

21
Це не глибока копія.
Сукрит Калра

1
Тоді що це. У ньому є два різних словника (ви можете перевірити ідентифікатори кожного) з однаковими значеннями.
tailor_raj

Прочитайте це , [:] просто створює дрібну копію, вона не створює рекурсивно копії об'єктів всередині одного.
Сукрит Калра

1
Дякую. ви маєте на увазі сказати, якщо ми використовуємо це, буде створений новий список, але всі елементи нового списку будуть лише копіями, вони матимуть такий самий об’єкт (той самий ідентифікатор), що і попередній?
tailor_raj

Спробуйте використовувати вкладений список. Оновіть вкладений елемент списку a. Він також буде оновлений у списку b. Це означає, що [:] не є глибокою копією.
AnupamChugh

2

просто рекурсивна функція глибокого копіювання.

def deepcopy(A):
    rt = []
    for elem in A:
        if isinstance(elem,list):
            rt.append(deepcopy(elem))
        else:
            rt.append(elem)
    return rt

Редагувати: Як згадував Cfreak, це вже реалізовано в copyмодулі.


4
Немає підстав повторно реалізовувати стандартну deepcopy()функцію в copyмодулі
Cfreak

1

Що стосується списку як дерева, то deep_copy в python може бути найбільш компактно записаний як

def deep_copy(x):
    if not isinstance(x, list): return x
    else: return map(deep_copy, x)


0

Це більш пітонічне

my_list = [0, 1, 2, 3, 4, 5]  # some list
my_list_copy = list(my_list)  # my_list_copy and my_list does not share reference now.

ПРИМІТКА. Це не є безпечним для переліку посилальних об'єктів


2
Це не працює. Я думав, що це може бути, але щойно перевірений. Спробуйте зі списком словників як хороший приклад
Shashank Singh

@ShashankSingh так, це не працюватиме для списку словників, оскільки записи є еталонними тегами (вказують на місце пам'яті). Таким чином, дублювання списку словника за допомогою цього методу створить новий список, але оскільки записи є словниками, вони все одно посилаються на те саме місце пам'яті.
Kwaw Annor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.