목록을 복제하거나 복사하는 방법?


2545

파이썬에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?

를 사용하는 동안 매번 변경 사항이 new_list = my_list수정됩니다 . 왜 이런거야?new_listmy_list

답변:


3326

를 사용하면 new_list = my_list실제로 두 개의 목록이 없습니다. 모두 이렇게 할당 그냥 복사 목록이 아닌 실제 목록에 대한 참조를, new_list그리고my_list 할당 한 후 동일한 목록을 참조하십시오.

실제로 목록을 복사하려면 다양한 가능성이 있습니다.

  • 내장 list.copy()메소드를 사용할 수 있습니다 (Python 3.3부터 사용 가능).

    new_list = old_list.copy()
  • 당신은 그것을 슬라이스 할 수 있습니다 :

    new_list = old_list[:]

    이것에 대한 Alex Martelli의 의견 (적어도 2007 년에 되돌아온 )은 그것이 이상한 구문이며 그것을 사용하는 것은 의미가 없다는 것입니다 . ;) (그의 의견으로는 다음 내용이 더 읽기 쉽다).

  • 내장 list()함수를 사용할 수 있습니다 :

    new_list = list(old_list)
  • 당신은 generic을 사용할 수 있습니다 copy.copy():

    import copy
    new_list = copy.copy(old_list)

    첫 번째 list()데이터 유형을 찾아야하기 때문에 이보다 약간 느립니다 old_list.

  • 목록에 객체가 포함되어 있고 복사하려는 경우 generic을 사용하십시오 copy.deepcopy().

    import copy
    new_list = copy.deepcopy(old_list)

    분명히 가장 느리고 가장 기억력이 좋은 방법이지만 때로는 피할 수없는 경우도 있습니다.

예:

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

결과:

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

7
내가 착각하지 않으면 : newlist = [*mylist]파이썬 3에서도 가능성 newlist = list(mylist)이 있습니다. 아마도 더 분명합니다.
Stéphane

9
또 다른 가능성은 new_list = old_list * 1
aris

4
이 방법들 중 어떤 방법이 얕은 사본이고 어떤 방법이 깊은 사본입니까?
Eswar

4
@ Eswar : 마지막 하나를 제외하고 모두 얕은 사본을
펠릭스 클링

3
@Eswar는 얕은 사본입니다.
juanpa.arrivillaga

602

Felix는 이미 훌륭한 답변을 제공했지만 다양한 방법을 속도 비교할 것이라고 생각했습니다.

  1. 10.59 초 (105.9us / itn)- copy.deepcopy(old_list)
  2. 10.16 초 (101.6us / itn)-순수 파이썬 Copy() 복사로 클래스를 복사하는 메소드
  3. 1.488 초 (14.88us / itn)-순수 파이썬 Copy() 클래스를 복사하지 않는 메소드 (dicts / lists / tuples 만)
  4. 0.325 초 (3.25us / itn)- for item in old_list: new_list.append(item)
  5. 0.217 초 (2.17us / itn)- [i for i in old_list]( 목록 이해 )
  6. 0.186 초 (1.86us / itn)- copy.copy(old_list)
  7. 0.075 초 (0.75us / itn)- list(old_list)
  8. 0.053 초 (0.53us / itn)- new_list = []; new_list.extend(old_list)
  9. 0.039 초 (0.39us / itn)- old_list[:]( 목록 슬라이싱 )

따라서 가장 빠른 것은 목록 슬라이싱입니다. 하지만 그 인식 copy.copy(), list[:]그리고 list(list)달리, copy.deepcopy()목록에있는 목록, 사전 및 클래스 인스턴스를 복사하지 않고 파이썬 버전은 원본이 변경되면, 그래서 그들도 그 반대 복사 목록에 변경됩니다.

(누군가 관심이 있거나 문제를 제기하려는 경우 스크립트는 다음과 같습니다.)

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

9
벤치마킹하기 때문에 참조 점을 포함시키는 것이 도움이 될 수 있습니다. 이 수치는 2017 년에 완전히 컴파일 된 코드와 함께 Python 3.6을 사용하여 여전히 정확합니까? 아래 답변 ( stackoverflow.com/a/17810305/26219 )이 이미이 답변에 의문을 제기하고 있습니다.
Mark Edington

4
timeit모듈을 사용하십시오 . 또한 이와 같은 임의의 마이크로 벤치 마크에서 많은 결론을 얻을 수는 없습니다.
Corey Goldberg

3
3.5+에 대한 새로운 옵션을 포함하려면 [*old_list]대략적으로와 동일해야 list(old_list)하지만 일반적인 함수 호출 경로가 아닌 구문이므로 런타임에 약간의 비용을 절약 old_list[:]할 수 있습니다. [*old_list]iterable에서 작동하고 list)를 생성합니다 .
ShadowRanger

3
약간 덜 임의적 인 마이크로 벤치 마크에 대한 @CoreyGoldberg ( timeit100k 대신 50m 실행) stackoverflow.com/a/43220129/3745896
River

1
@ShadowRanger는 [*old_list]실제로 거의 다른 방법보다 성능이 뛰어납니다. (이전 의견에 링크 된 내 답변 참조)
River

150

내가 한 말되었다 파이썬 3.3 이상이 있음을 추가list.copy() 슬라이스 한 빨리해야한다 방법을 :

newlist = old_list.copy()


6
네, 그리고 문서에 따라 docs.python.org/3/library/stdtypes.html#mutable-sequence-types , s.copy()의 단순 복사본을 생성한다 s(같은 동일 s[:]).
CyberMew

실제로 현재, 보인다 python3.8, .copy()이다 약간 빠른 슬라이스보다. 아래 @AaronsHall 답변을 참조하십시오.
loved.by.Jesus

125

파이썬에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?

Python 3에서는 다음을 사용하여 얕은 사본을 만들 수 있습니다.

a_copy = a_list.copy()

파이썬 2와 3에서는 원본의 전체 조각으로 얕은 사본을 얻을 수 있습니다.

a_copy = a_list[:]

설명

리스트를 복사하는 두 가지 의미 론적 방법이 있습니다. 얕은 사본은 동일한 오브젝트의 새 목록을 작성하고 깊은 사본은 새로운 동등한 오브젝트를 포함하는 새 목록을 작성합니다.

얕은 목록 사본

단순 복사본은 목록 자체 만 복사합니다. 목록 자체는 목록의 개체에 대한 참조 컨테이너입니다. 포함 된 개체가 변경 가능하고 하나가 변경되면 변경 내용이 두 목록에 모두 반영됩니다.

파이썬 2와 3에서는 이것을하는 다른 방법이 있습니다. 파이썬 2는 파이썬 3에서도 작동합니다.

파이썬 2

Python 2에서 목록의 얕은 사본을 만드는 관용적 방법은 원본의 완전한 조각입니다.

a_copy = a_list[:]

목록 생성자를 통해 목록을 전달하여 동일한 작업을 수행 할 수도 있습니다.

a_copy = list(a_list)

그러나 생성자를 사용하는 것이 덜 효율적입니다.

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

파이썬 3

파이썬 3에서리스트는 list.copy메소드를 얻습니다 :

a_copy = a_list.copy()

파이썬 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

다른 포인터를 만들면 복사 되지 않습니다

new_list = my_list를 사용하면 my_list가 변경 될 때마다 new_list가 수정됩니다. 왜 이런거야?

my_list메모리의 실제 목록을 가리키는 이름 일뿐입니다. new_list = my_list복사하지 않는다고 말하면 메모리의 원래 목록을 가리키는 다른 이름을 추가하는 것입니다. 목록을 복사 할 때 비슷한 문제가 발생할 수 있습니다.

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

목록은 내용에 대한 포인터 배열이므로 얕은 사본은 포인터를 복사하기 때문에 두 개의 다른 목록이 있지만 내용은 동일합니다. 내용을 복사하려면 깊은 사본이 필요합니다.

딥 카피

파이썬 2 또는 3에서 목록deepcopycopy깊은 사본 을 만들려면 모듈 에서 사용 하십시오 .

import copy
a_deep_copy = copy.deepcopy(a_list)

이것이 어떻게 새로운 하위 목록을 만들 수 있는지 보여주기 위해 :

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

따라서 우리는 딥 카피 목록이 원본과 완전히 다른 목록임을 알 수 있습니다. 당신은 자신의 기능을 굴릴 수는 있지만 그렇지 않습니다. 표준 라이브러리의 딥 카피 기능을 사용하면 그렇지 않은 버그를 만들 수 있습니다.

사용하지 마십시오 eval

이것을 심도 복사 방법으로 사용했을 수도 있지만 그렇게하지 마십시오.

problematic_deep_copy = eval(repr(a_list))
  1. 특히 신뢰할 수없는 출처에서 무언가를 평가하는 경우 위험합니다.
  2. 복사하는 하위 요소에 동등한 요소를 재현하기 위해 평가할 수있는 표현이없는 경우 신뢰할 수 없습니다.
  3. 또한 성능이 떨어집니다.

64 비트 Python 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

64 비트 Python 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

1
목록이 2D 인 경우 딥 카피가 필요하지 않습니다. 목록의 목록이고 해당 목록에 목록이없는 경우 for 루프를 사용할 수 있습니다. 현재, 나는 사용 list_copy=[] for item in list: list_copy.append(copy(item))하고 있으며 훨씬 빠릅니다.
John Locke

53

올바른 사본을 만드는 방법을 알려주는 많은 답변이 이미 있지만, 왜 '원본'복사가 실패했는지는 아무도 대답하지 않습니다.

파이썬은 변수에 값을 저장하지 않습니다. 이름을 객체에 바인딩합니다. 원래 과제는 참조한 객체를 가져 와서 my_list바인딩했습니다 new_list. 어떤 이름을 사용하든 여전히 하나의 목록 만 있으므로이를 참조 할 때 작성된 변경 사항은로 참조 my_list될 때 유지됩니다 new_list. 이 질문에 대한 다른 답변은 서로 바인딩 할 새 객체를 만드는 다양한 방법을 제공합니다 new_list.

목록의 각 요소는 이름과 같은 역할을합니다. 각 요소는 배타적으로 개체에 바인딩됩니다. 얕은 복사본은 요소가 이전과 동일한 개체에 바인딩되는 새 목록을 만듭니다.

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]

목록 복사를 한 단계 더 진행하려면 목록이 참조하는 각 개체를 복사하고 해당 요소 복사본을 새 목록에 바인딩하십시오.

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

목록의 각 요소가 해당 요소에 바인딩 된 것처럼 목록의 각 요소가 다른 개체를 참조 할 수 있기 때문에 아직 딥 카피는 아닙니다. 목록의 모든 요소를 ​​재귀 적으로 복사 한 다음 각 요소가 참조하는 서로 다른 개체 등을 반복적으로 복사하려면 다음과 같이하십시오. 깊은 복사를 수행하십시오.

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

복사시 코너 케이스에 대한 자세한 내용은 설명서 를 참조하십시오 .


37

사용하다 thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

34

처음부터 시작하여이 질문을 살펴 봅시다.

따라서 두 개의 목록이 있다고 가정 해 봅시다.

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

그리고 이제 첫 번째 목록부터 시작하여 두 목록을 모두 복사해야합니다.

먼저 변수 copy를 원래 목록 으로 설정해 봅시다 list_1.

copy=list_1

이제 copy가 list_1을 복사했다고 생각하면 잘못되었습니다. 이 id함수는 두 변수가 같은 객체를 가리킬 수 있는지 보여줍니다. 이것을 시도하자 :

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

출력은 다음과 같습니다.

4329485320
4329485320

두 변수는 모두 동일한 인수입니다. 너 놀랐 니?

파이썬이 변수에 아무것도 저장하지 않는다는 것을 알기 때문에 변수는 객체를 참조하고 객체는 값을 저장합니다. 여기 객체는list 이지만 두 개의 다른 변수 이름으로 동일한 객체에 대한 두 개의 참조를 만들었습니다. 이것은 두 변수가 다른 이름을 가진 동일한 객체를 가리키고 있음을 의미합니다.

당신이 할 때 copy=list_1실제로하고 있습니다 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 list_1과 copy는 두 개의 변수 이름이지만 객체는 두 변수에 대해 동일합니다. list

따라서 복사 된 목록을 수정하려고하면 목록이 하나이기 때문에 원본 목록도 수정됩니다. 복사 된 목록 또는 원본 목록에서 수행 한 목록에 관계없이 해당 목록을 수정합니다.

copy[0]="modify"

print(copy)
print(list_1)

산출:

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

그래서 원래 목록을 수정했습니다.

이제리스트를 복사하기위한 pythonic 방법으로 넘어 갑시다.

copy_1=list_1[:]

이 방법은 첫 번째 문제를 해결합니다.

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

4338792136
4338791432

우리가 볼 수 있듯이 두 목록이 다른 ID를 가지고 있다는 것은 두 변수가 다른 객체를 가리키고 있음을 의미합니다. 실제로 여기서 진행되는 것은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

이제 목록을 수정하고 이전 문제가 계속 발생하는지 확인해 보겠습니다.

copy_1[0]="modify"

print(list_1)
print(copy_1)

출력은 다음과 같습니다.

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

보시다시피 복사 된 목록 만 수정되었습니다. 그것은 그것이 효과가 있다는 것을 의미합니다.

우리가 끝났다고 생각합니까? 아니요. 중첩 목록을 복사 해 봅시다.

copy_2=list_2[:]

list_2의 사본 인 다른 객체를 참조해야합니다 list_2. 점검 해보자:

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

우리는 출력을 얻습니다.

4330403592 4330403528

이제 두 목록이 다른 객체를 가리키고 있다고 가정 할 수 있습니다. 이제 수정하고 우리가 원하는 것을주는 것을 보자.

copy_2[0][1]="modify"

print(list_2,copy_2)

결과는 다음과 같습니다.

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

이전에 사용한 것과 같은 방법으로 작동했기 때문에 약간 혼란스러워 보일 수 있습니다. 이것을 이해하려고 노력합시다.

할 때 :

copy_2=list_2[:]

내부 목록이 아닌 외부 목록 만 복사하고 있습니다. 우리는 사용할 수 있습니다id 기능을 다시 한 번 이를 확인할 .

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

출력은 다음과 같습니다.

4329485832
4329485832

우리가 할 때 copy_2=list_2[:]이런 일이 발생합니다.

여기에 이미지 설명을 입력하십시오

중첩 된 목록 복사본이 아닌 목록의 사본을 생성하지만 외부 목록 사본 만 생성합니다. 중첩 된 목록은 두 변수에 대해 동일하므로 중첩 된 목록을 수정하려고하면 중첩 된 목록 객체가 동일하므로 원래 목록도 수정됩니다 두 목록 모두.

해결 방법이 무엇입니까? 해결책은 deepcopy기능입니다.

from copy import deepcopy
deep=deepcopy(list_2)

이것을 확인하자 :

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

4322146056 4322148040

두 외부 목록은 서로 다른 ID를 가지고 있으므로 내부 중첩 목록에서 시도해 봅시다.

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

출력은 다음과 같습니다.

4322145992
4322145800

보시다시피 두 ID가 다름을 알 수 있습니다. 즉, 중첩 된 두 목록이 모두 다른 개체를 가리키고 있다고 가정 할 수 있습니다.

이것은 deep=deepcopy(list_2)실제로 일어나는 일을 의미 합니다.

여기에 이미지 설명을 입력하십시오

두 중첩 목록이 서로 다른 객체를 가리키고 있으며 이제 중첩 목록의 별도 사본이 있습니다.

이제 중첩 목록을 수정하여 이전 문제가 해결되었는지 확인하십시오.

deep[0][1]="modify"
print(list_2,deep)

출력합니다 :

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

보시다시피 원래 중첩 목록은 수정하지 않고 복사 된 목록 만 수정했습니다.



33

파이썬 3.6 타이밍

다음은 Python 3.6.8을 사용한 타이밍 결과입니다. 이 시간은 절대적이 아니라 서로에 대한 것임을 명심하십시오.

얕은 사본 만 사용하고 Python2에서는 불가능했던 몇 가지 새로운 메소드 list.copy()( 예 : Python3 slice equivalent )와 두 가지 형식의 목록 압축 풀기 ( *new_list, = listnew_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

우리는 Python2 우승자가 여전히 잘하지만 Python3을 능가하지는 않는다는 것을 알 수 있습니다 list.copy() , 특히 후자의 우수한 가독성을 고려할 때 을 많이 .

다크 호스는 포장 풀기 및 재 포장 방법 ( b = [*a])으로, 원시 슬라이싱보다 ~ 25 % 빠르며 다른 포장 풀기 방법보다 2 배 빠릅니다 (*b, = a ) .

b = a * 1 놀랍게도 잘합니다.

이러한 방법은 목록 이외의 입력에 대해 동등한 결과를 출력 하지 않습니다 . 그것들은 모두 슬라이스 가능한 객체에서 작동하고, 반복 가능한 일부에서는 copy.copy()작동 하지만 더 일반적인 파이썬 객체 에서만 작동합니다.


이해 당사자를위한 테스트 코드는 다음과 같습니다 ( 여기에서 템플릿 ).

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

1
3.8에서 비슷한 이야기를 확인할 수 있습니다 b=[*a]-명백한 방법;).
슈퍼 슈트

19

다른 모든 기고자들은 훌륭한 답변을 냈습니다. 단일 차원 (레벨) 목록이있을 때 작동하지만 지금까지 언급 한 방법 중 copy.deepcopy()하나는 목록을 복제 / 복사하고 중첩 된 list객체를 가리 키지 않도록합니다. 다차원의 중첩 목록 (목록 목록)으로 작업. Felix Kling 이 그의 답변에서 언급 하지만 ,이 문제에 대해서는 조금 더 자세한 내용이 있으며 기본 제공 기능을 사용하여보다 빠른 대안을 제시 할 수있는 해결 방법이 있습니다 deepcopy.

반면 new_list = old_list[:], copy.copy(old_list)'및 Py3k에 대한 old_list.copy()단일 수평 목록에 대한 작업, 그들은 가리키는으로 되돌아 list내에 중첩 된 객체 old_listnew_list의 하나로, 변경list 객체는 다른에 영속되어있다.

편집 : 새로운 정보가 등장

Aaron HallPM 2Ring 이 지적한 바와 같이 사용 eval()하는 것은 나쁜 생각 일뿐 만 아니라보다 느립니다 copy.deepcopy().

이는 다차원 목록의 경우 유일한 옵션은 copy.deepcopy()입니다. 말했듯이, 적당한 크기의 다차원 배열에서 성능을 사용하려고 할 때 성능이 남쪽으로 진행되므로 실제로 옵션이 아닙니다. 나는 노력했다timeit 생물 정보학 응용 프로그램에 대해 들어 보지 못했거나 그다지 큰 42x42 배열 사용 응답을 기다리는 것을 포기 하고이 게시물에 편집 내용을 입력하기 시작했습니다.

그렇다면 유일한 유일한 옵션은 여러 목록을 초기화하고 독립적으로 작업하는 것입니다. 다차원 목록 복사를 처리하는 방법에 대한 다른 제안이 있으면 누구나 감사하겠습니다.

다른 사람들이 언급했듯이, 모듈을 사용하고 다차원 목록을 사용하는 경우 상당한 성능 문제 가 있습니다 .copycopy.deepcopy


5
반환 된 문자열 repr()이 객체를 다시 생성하기에 충분하다는 보장이 없기 때문에 항상 작동하지는 않습니다 . 또한 eval()최후의 수단입니다. 자세한 내용 SO 베테랑 Ned Batchelder에 의해 Eval이 실제로 위험 함을 참조하십시오. 당신이 사용을 옹호 할 때 그래서 eval()당신은 정말 이 위험 할 수 있다는 것을 언급해야한다.
PM 2Ring

1
페어 포인트. Batchelder의 요점은 eval()파이썬에서 일반적으로 함수를 갖는 것이 위험 하다는 것입니다 . 코드에서 함수를 사용하는지 여부는 많지 않지만 파이썬 자체의 보안 허점입니다. 예 내에서 입력을 수신하는 기능을 사용하지 않는 input(), sys.agrv또는 심지어 텍스트 파일. 빈 다차원 목록을 한 번 초기화 한 다음 루프의 각 반복에서 다시 초기화하는 대신 루프에서 복사하는 방법이 있습니다.
AMR

1
@AaronHall이 지적했듯이을 사용하는 데 중요한 성능 문제가있을 수 new_list = eval(repr(old_list))있으므로 나쁜 생각 외에도 작동하기가 너무 느릴 수 있습니다.
AMR

12

이것이 아직 언급되지 않았다는 사실에 놀랍습니다. 따라서 완전성을 위해 ...

"splat operator":를 사용하여 목록 압축 풀기를 수행 할 수 있으며 *, 목록의 요소도 복사됩니다.

old_list = [1, 2, 3]

new_list = [*old_list]

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

이 방법의 명백한 단점은 Python 3.5 이상에서만 사용할 수 있다는 것입니다.

그러나 현명한 타이밍은 다른 일반적인 방법보다 성능이 우수합니다.

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)

1
사본을 수정할 때이 방법은 어떻게 작동합니까?
not2qubit

2
@ not2qubit는 새 목록의 요소를 추가하거나 편집하는 것을 의미합니까? 예에서 old_listnew_list두 개의 서로 다른 목록 (이러한 방법-중 어느 것도 전체 복사본 없습니다, 직접 이러한 목록의 목록으로 요소 자체 (돌연변이하지 않는 한)) 하나가 다른 변경되지 않습니다 편집이다.
SCB

7

파이썬 버전과 독립적 인 매우 간단한 접근법은 이미 주어진 대답에서 누락되어 대부분의 시간을 사용할 수 있습니다 (적어도 나는합니다).

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

그러나 my_list에 다른 컨테이너 (예 : 중첩 목록)가 포함되어 있으면 위의 답변에서 복사 라이브러리에서 제안 된 다른 것과 같이 deepcopy를 사용해야합니다. 예를 들면 다음과 같습니다.

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

. 보너스 : 요소를 복사하지 않으려면 (얕은 복사)를 사용하십시오.

new_list = my_list[:]

솔루션 # 1과 솔루션 # 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])

보시다시피 솔루션 # 1은 중첩 목록을 사용하지 않을 때 완벽하게 작동했습니다. 솔루션 # 1을 중첩 된 목록에 적용 할 때 어떤 일이 발생하는지 확인하겠습니다.

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

7

자신의 사용자 정의 클래스를 정의하고 속성을 유지하려는 경우 Python 3과 같은 대안 대신 copy.copy()또는 copy.deepcopy()대신 사용해야 하는 경우 가 있습니다 .

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

출력 :

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list

4
new_list = my_list[:]

new_list = my_list 이것을 이해하려고 노력하십시오. my_list가 X 위치의 힙 메모리에 있다고 가정합니다. 즉, my_list가 X를 가리키고 있습니다.new_list = my_list new_list를 X를 가리 키도록 합니다. 이것을 얕은 복사라고합니다.

이제 할당하면 new_list = my_list[:]my_list의 각 객체를 new_list로 복사하기 만하면됩니다. 이것을 딥 카피라고합니다.

이 작업을 수행 할 수있는 다른 방법은 다음과 같습니다.

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)

2

다른 답변과 약간 다른 것을 게시하고 싶었습니다. 이것이 가장 이해하기 쉽지 않거나 가장 빠른 옵션은 아니지만 딥 카피의 작동 방식에 대한 약간의 내부 견해와 딥 카피의 또 다른 대체 옵션입니다. 내 함수에 버그가 있는지 여부는 중요하지 않습니다.이 시점의 핵심은 질문 답변과 같은 객체를 복사하는 방법을 보여주는 것이 아니라 심층 복사가 핵심에서 어떻게 작동하는지 설명하는 포인트로 사용하기 때문입니다.

깊은 복사 기능의 핵심은 얕은 복사를 만드는 방법입니다. 어떻게? 단순한. 깊은 복사 기능은 변경 불가능한 객체의 컨테이너 만 복제합니다. 중첩 된 목록을 딥 카피하면 목록 내부의 변경 가능한 개체가 아닌 외부 목록 만 복제됩니다. 컨테이너를 복제하는 중입니다. 수업에서도 마찬가지입니다. 클래스를 딥 카피 할 때 변경 가능한 모든 속성을 딥 카피합니다. 그래서 방법? 목록, dict, tuple, iter, 클래스 및 클래스 인스턴스와 같은 컨테이너 만 어떻게 복사해야합니까?

간단 해. 변경 가능한 객체는 실제로 복제 할 수 없습니다. 변경할 수 없으므로 단일 값일뿐입니다. 즉, 문자열, 숫자, 부울 또는 그중 하나를 복제 할 필요가 없습니다. 그러나 컨테이너를 어떻게 복제합니까? 단순한. 모든 값으로 새 컨테이너를 초기화하십시오. Deepcopy는 재귀에 의존합니다. 컨테이너가 남아 있지 않을 때까지 모든 컨테이너, 컨테이너가 내부에있는 컨테이너를 복제합니다. 컨테이너는 불변의 객체입니다.

일단 알면 참조없이 객체를 완전히 복제하는 것은 매우 쉽습니다. 기본 데이터 유형을 심층 복사하는 기능은 다음과 같습니다 (사용자 정의 클래스에서는 작동하지 않지만 항상 추가 할 수는 있습니다)

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

파이썬 자체 내장 심도 복사는이 예제를 기반으로합니다. 유일한 차이점은 다른 유형을 지원하고 속성을 새로운 복제 클래스로 복제하여 사용자 클래스를 지원하며 메모 목록 또는 사전을 사용하여 이미 본 객체에 대한 참조로 무한 재귀를 차단합니다. 그리고 그것은 깊은 사본을 만들기위한 것입니다. 기본적으로 딥 카피는 얕은 카피를 만드는 것입니다. 이 답변이 질문에 무언가를 추가하기를 바랍니다.

실시 예

[1, 2, 3] 목록이 있다고 가정하십시오 . 불변의 숫자는 복제 할 수 없지만 다른 레이어는 복제 할 수 없습니다. 리스트 이해를 사용하여 복제 할 수 있습니다. [[x in x in [1, 2, 3]

이제 [[1, 2], [3, 4], [5, 6]] 목록이 있다고 가정하십시오 . 이번에는 재귀를 사용하여 목록의 모든 레이어를 딥 카피하는 함수를 만들고 싶습니다. 이전 목록 이해 대신 :

[x for x in _list]

목록에 새로운 것을 사용합니다.

[deepcopy_list(x) for x in _list]

그리고 deepcopy_list 모습이 맘에 :

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

그런 다음 재귀를 사용하여 strs, bools, floast, intlist 를 무한히 많은 레이어에 심층 복사 할 수있는 기능이 있습니다 . 그리고 거기에는 심층 복사가 있습니다.

TLDR : Deepcopy는 재귀를 사용하여 객체를 복제하며 불변 객체는 복제 할 수 없으므로 이전과 동일한 불변 객체를 반환합니다. 그러나 객체의 가장 바깥쪽에있는 변경 가능 레이어에 도달 할 때까지 가변 객체의 가장 안쪽 레이어를 심층 복사합니다.


2

id와 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) 

2

파이썬에서는 다음을 기억하십시오.

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

List2는 실제 목록을 저장하지 않고 list1에 대한 참조를 저장합니다. 따라서 list1에 대한 작업을 수행하면 list2도 변경됩니다. 복사 모듈 (기본값은 아님, pip로 다운로드)을 사용하여 목록의 원본을 만듭니다 ( copy.copy()단순한 목록, copy.deepcopy()중첩 된 목록의 경우 ). 첫 번째 목록으로 변경되지 않은 사본을 만듭니다.


0

deepcopy 옵션은 나를 위해 작동하는 유일한 방법입니다.

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('-----------------------------')

다음과 같은 출력으로 이어집니다.

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]]]
-----------------------------
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.