파이썬에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?
를 사용하는 동안 매번 변경 사항이 new_list = my_list
수정됩니다 . 왜 이런거야?new_list
my_list
답변
를 사용하면 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)]
답변
Felix는 이미 훌륭한 답변을 제공했지만 다양한 방법을 속도 비교할 것이라고 생각했습니다.
- 10.59 초 (105.9us / itn)-
copy.deepcopy(old_list)
- 10.16 초 (101.6us / itn)-순수 파이썬
Copy()
복사로 클래스를 복사하는 메소드 - 1.488 초 (14.88us / itn)-순수 파이썬
Copy()
클래스를 복사하지 않는 메소드 (dicts / lists / tuples 만) - 0.325 초 (3.25us / itn)-
for item in old_list: new_list.append(item)
- 0.217 초 (2.17us / itn)-
[i for i in old_list]
( 목록 이해 ) - 0.186 초 (1.86us / itn)-
copy.copy(old_list)
- 0.075 초 (0.75us / itn)-
list(old_list)
- 0.053 초 (0.53us / itn)-
new_list = []; new_list.extend(old_list)
- 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
답변
답변
파이썬에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?
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에서 목록deepcopy
copy
의 깊은 사본 을 만들려면 모듈 에서 사용 하십시오 .
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))
- 특히 신뢰할 수없는 출처에서 무언가를 평가하는 경우 위험합니다.
- 복사하는 하위 요소에 동등한 요소를 재현하기 위해 평가할 수있는 표현이없는 경우 신뢰할 수 없습니다.
- 또한 성능이 떨어집니다.
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
답변
올바른 사본을 만드는 방법을 알려주는 많은 답변이 이미 있지만, 왜 ‘원본’복사가 실패했는지는 아무도 대답하지 않습니다.
파이썬은 변수에 값을 저장하지 않습니다. 이름을 객체에 바인딩합니다. 원래 과제는 참조한 객체를 가져 와서 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)
복사시 코너 케이스에 대한 자세한 내용은 설명서 를 참조하십시오 .
답변
사용하다 thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
답변
처음부터 시작하여이 질문을 살펴 봅시다.
따라서 두 개의 목록이 있다고 가정 해 봅시다.
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']]
보시다시피 원래 중첩 목록은 수정하지 않고 복사 된 목록 만 수정했습니다.