할당 후 예기치 않게 변경되지 않도록 목록을 복제하려면 어떻게 해야 합니까?
중new_list = my_list
에 대한 모든 , " " " "new_list
사항 ''"my_list
이며, 방지하기 위해 을 복제 해야 합니까?그 이유와 이를 방지하기 위해 목록을 복제하거나 복사하려면 어떻게 해야 합니까?
new_list = my_list
실제로는 두 번째 목록을 작성하지 않습니다.는 실제둘 다 복사해야 합니다.실제 리스트는 복사하지 않기 때문에 둘 다new_list
★★★★★★★★★★★★★★★★★」my_list
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
실제로 목록을 복사하려면 다음과 같은 몇 가지 옵션을 사용할 수 있습니다.
내장된 메서드를 사용할 수 있습니다(Python 3.3 이후 사용 가능).
new_list = old_list.copy()
슬라이스 할 수 있습니다.
new_list = old_list[:]
이것에 대한 Alex Martelli의 견해(적어도 2007년은)는, 이것은 이상한 구문이며, 그것을 사용하는 것은 의미가 없다고 하는 것입니다.;)(그의 의견으로는, 다음 구문이 더 읽기 쉽다고 생각합니다.
빌트인 컨스트럭터를 사용할 수 있습니다.
new_list = list(old_list)
범용을 사용할 수 있습니다.
import copy new_list = copy.copy(old_list)
은 조금 .
list()
이유는 '아까부터'의 유형을 하기 때문입니다.old_list
번째목록의 요소도 복사해야 할 경우 generic을 사용합니다.
import copy new_list = copy.deepcopy(old_list)
확실히 가장 느리고 가장 기억을 필요로 하는 방법이지만 때로는 피할 수 없는 경우도 있습니다.이것은 재귀적으로 동작하며, 임의의 수의 중첩된 목록(또는 다른 컨테이너) 수준을 처리합니다.
예:
import copy
class Foo(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return f'Foo({self.val!r})'
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(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {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.9µs/itn) -
- - Python 10.16초(101.6µs/itn) - Python
Copy()
deepcopy deepcopy를 사용한 클래스 - 1 - Python 1.488µ(14.88µs/itn) - Python
Copy()
(눈금/목록/튜플) - - 0.140µ(3.25µs/itn) -
for item in old_list: new_list.append(item)
- - 0.217µs(2.17µs/itn) -
[i for i in old_list]
(목록 이해) - 0.86µs/itn (1.86µs/itn) -
- - 0.075µ(0.75µs/itn) -
list(old_list)
- - 0.053µs(0.53µs/itn) -
new_list = []; new_list.extend(old_list)
- - 0.039µs(0.39µs/itn) -
old_list[:]
(목록 슬라이스)
그래서 가장 빠른 것은 리스트 슬라이스입니다., ★★★★★★★★★★★★★★★★★★★★★★★★★★」copy.copy()
,list[:]
★★★★★★★★★★★★★★★★★」list(list)
는, 「」와 달리, 「」는 다릅니다.copy.deepcopy()
python 버전은 목록, 사전 및 클래스 인스턴스를 복사하지 않으므로 원본이 변경되면 복사된 목록에서도 변경됩니다.
(관심이 있거나 문제를 제기하고 싶은 사람은 다음과 같습니다.)
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.3+는 슬라이스만큼 빠른 메서드를 추가한다고 들었습니다.
newlist = old_list.copy()
Python에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?
Python 3 에서는, 다음과 같이 얕은 카피를 작성할 수 있습니다.
a_copy = a_list.copy()
Python 2와 3에서는 원본의 전체 슬라이스로 얕은 복사본을 얻을 수 있습니다.
a_copy = a_list[:]
설명.
목록을 복사하는 의미적 방법은 두 가지가 있습니다.얕은 복사본은 동일한 개체의 새 목록을 생성하고, 깊은 복사본은 동등한 새 개체를 포함하는 새 목록을 만듭니다.
얕은 목록 복사
얕은 복사본은 목록 내의 개체에 대한 참조 컨테이너인 목록 자체만 복사합니다.포함된 개체 자체가 변경 가능하고 하나를 변경하면 변경 내용이 두 목록에 모두 반영됩니다.
Python 2와 Python 3에서는 다양한 방법이 있습니다.Python 2 ways는 Python 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
가 Python 3을 합니다.list.copy
★★★★
a_copy = a_list.copy()
Python 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'], [], []]
목록은 내용에 대한 포인터의 배열일 뿐이므로 얕은 복사본은 포인터를 복사하기만 하면 됩니다. 따라서 두 개의 다른 목록이 있지만 내용은 동일합니다.내용을 복사하려면 심층 복사가 필요합니다.
상세 복사
목록을 자세히 복사하려면 Python 2 또는 3에서 모듈에서 다음을 사용합니다.
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
그럼 처음부터 이 문제를 살펴볼까요?
다음 두 가지 목록이 있다고 가정합니다.
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
두 변수는 완전히 동일한 인수입니다.너 놀랐냐?
아시다시피 Python은 변수에 아무것도 저장하지 않습니다. 변수는 객체와 객체가 값을 저장하는 것에 불과합니다.는 " " " 입니다.list
같은 오브젝트에 대한 참조를 2개의 다른 변수 이름으로 작성했습니다.즉, 두 변수가 서로 다른 이름으로 동일한 개체를 가리키고 있습니다.
때 ★★★★copy = list_1
하고 .
여기 이미지 list_1과 copy는 2개의 변수명이지만 오브젝트는 다음 두 변수 모두 동일합니다.list
.
따라서 복사된 목록을 수정하려고 하면 목록만 복사되어 있기 때문에 원래 목록도 수정됩니다. 복사된 목록 또는 원본 목록에서 수정되어도 해당 목록이 수정됩니다.
copy[0] = "modify"
print(copy)
print(list_1)
출력:
['modify', '98']
['modify', '98']
따라서 원래 목록을 수정했습니다.
이제 목록을 복사하는 피톤식 방법으로 넘어가겠습니다.
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가 서로 다르므로 두 중첩된 목록이 서로 다른 개체를 가리키고 있다고 가정할 수 있습니다.
, ,, 어, 어, 어, 어, 어, 어, this, this, this,deep = deepcopy(list_2)
" " " 어어어 。
두 중첩 목록이 서로 다른 개체를 가리키고 있으며 현재 중첩 목록의 개별 복사본이 있습니다.
이제 네스트된 목록을 수정하여 이전 문제를 해결했는지 확인합니다.
deep[0][1] = "modify"
print(list_2, deep)
출력:
[['01', '98']] [['01', 'modify']]
보시다시피 원래 중첩 목록은 수정하지 않고 복사된 목록만 수정했습니다.
적절한 복사본을 만드는 방법을 알려주는 답변은 이미 많이 있지만, 원본 '복사'가 실패한 이유를 설명하는 답변은 없습니다.
Python은 변수에 값을 저장하지 않고 개체에 이름을 바인딩합니다.원래 할당은 에서 참조하는 개체를 가져왔습니다.my_list
그리고 그것을 에 묶었다.new_list
밖에 없기 에 1번, 1번, 1번, 1번, 1번, 3번, 3번, 3번, 3번, 3번, 3번, 3번, 3번, , 3번, 3번, 3번, 3번, 3번, 4번, 3번, 4번, 4번, 4번, 4번, 4번, 4번, 4번, 3번, 4번, 4번, 4번, 4번, 4번, 4번, 3번, 4번, 4번, 4번, 4번, 4번, 4번, 4번, 4번, 4번, 4번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]
>>>
Python 3.6 타이밍
다음은 Python 3.6.8을 사용한 타이밍 결과입니다.이 시간들은 절대적인 것이 아니라 서로 상대적인 것이라는 것을 명심하세요.
카피만을 하고, 할 수 했습니다.list.copy()
(Python 3 슬라이스 상당) 및 두 가지 형태의 목록 언팩(*new_list, = list
★★★★★★★★★★★★★★★★★」new_list = [*list]
METHOD TIME TAKEN
b = [*a] 2.75180600000021
b = a * 1 3.50215399999990
b = a[:] 3.78278899999986 # Python 2 winner (see above)
b = a.copy() 4.20556500000020 # Python 3 "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
2 3 Python 2 우승자는 Python 3에 않습니다.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))
는 파이썬이다.newList = oldList[:]
다른 모든 기여자는 훌륭한 답변을 했습니다. 이는 단일 차원(레벨) 리스트가 있는 경우에 유효하지만, 지금까지 언급한 방법 중 하나만 해당됩니다.copy.deepcopy()
의 클론/해, 된 「리스트/카피」를 .list
오브젝트(다차원 중첩 목록(목록 목록)을 사용하는 경우)Felix Kling은 답변에서 이 문제를 언급하고 있지만, 이 문제에는 조금 더 많은 문제가 있습니다.또, 빌트인을 사용하는 것에 의해서, 보다 신속한 대체 방법이 실현될 가능성이 있습니다.deepcopy
.
한편, 「 」는, 「 」, 「 」의 사이에new_list = old_list[:]
,copy.copy(old_list)'
3 Py3k py 경 경 경 경old_list.copy()
의 리스트로 하면, 「 리스트」의 「일부 리스트」를 하는 .list
old_list
및new_list
을 .list
오브젝트는 다른 오브젝트에 영속됩니다.
편집: 새로운 정보가 공개되다
Aaron Hall과 PM 2Ring 모두 지적했듯이 사용하는 것은 좋지 않을 뿐만 아니라 보다 훨씬 느립니다.
, 리스트의 경우 은 ', '다차원 리스트'입니다.
copy.deepcopy()
그렇다고 해서 적당한 크기의 다차원 어레이에서 사용하려고 하면 성능이 크게 저하되기 때문에 이 방법은 선택사항이 아닙니다.timeit
어레이를 대해 큰 , 것을 포스트에 하기 시작했습니다.42x42 입니다.그리고 저는 응답을 기다리는 것을 포기하고 이 포스트에 제 편집을 입력하기 시작했습니다.따라서 실제 옵션은 여러 목록을 초기화하고 독립적으로 작업하는 것 뿐입니다.다차원 목록 복사 처리 방법에 대해 다른 제안이 있으시면 감사하겠습니다.
다른 사용자가 언급했듯이 이 기능을 사용하는 경우 심각한 성능 문제가 발생합니다.copy
및 "discommand" (모듈)copy.deepcopy
다차원 목록의 경우.
이게 아직 언급되지 않았다는 게 놀랍네요, 그래서 완전성을 위해서...
"splat 연산자"를 사용하여 목록 압축을 해제할 수 있습니다.*
목록 요소도 복사합니다.
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)
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)
python 버전과는 무관한 매우 간단한 접근법이 이미 주어진 답변에 누락되어 있습니다.이 답변은 대부분의 시간을 사용할 수 있습니다(적어도 저는 사용할 수 있습니다).
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
.Bonus: 요소를 복사하지 않는 경우(AKA 얕은 복사):
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
다른 답안들과는 조금 다른 글을 올리고 싶었어요.가장 이해하기 쉽거나 가장 빠른 옵션은 아닐 수 있지만, 심층 복사가 어떻게 이루어지는지에 대한 내부 뷰를 제공할 뿐만 아니라 심층 복사를 위한 또 다른 옵션이기도 합니다.내 함수에 버그가 있는지는 중요하지 않습니다.왜냐하면 질문의 답변과 같은 오브젝트를 복사하는 방법을 보여주는 것이 포인트이기 때문입니다.또, 이것을 핵심에서 deepcopy가 어떻게 동작하는지를 설명하는 포인트로 사용합니다.
딥 카피 기능의 핵심은 얕은 카피를 만드는 방법입니다. 어떻게요?간단하죠.모든 딥 복사 기능은 불변 객체의 컨테이너만 복제합니다.중첩된 목록을 자세히 복사하면 목록 내의 가변 개체가 아닌 외부 목록만 복제됩니다.컨테이너만 복제하고 있습니다.수업에서도 마찬가지입니다.클래스를 딥 카피하면, 모든 변경 가능한 속성이 딥 카피됩니다.어떻게요?목록, 딕트, 튜플, 반복기, 클래스 및 클래스 인스턴스 등의 컨테이너만 복사하면 되는 이유는 무엇입니까?
간단해요.가변 객체는 실제로 복제할 수 없습니다.이 값은 변경할 수 없으므로 단일 값일 뿐입니다.즉, 문자열, 숫자, 부울 또는 그 어떤 것도 복제할 필요가 없습니다.하지만 어떻게 컨테이너를 복제할 수 있을까요?간단하죠.모든 값을 사용하여 새 컨테이너를 초기화하기만 하면 됩니다.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))
Python의 내장 딥카피는 이 예를 기반으로 합니다.유일한 차이점은 다른 유형을 지원하고 속성을 새 중복 클래스로 복제하여 사용자 클래스를 지원하며 메모 목록 또는 사전을 사용하여 이미 확인된 개체에 대한 참조를 통해 무한 재귀도 차단한다는 것입니다.깊이 있는 복사는 이것으로 끝입니다.본질적으로 깊이 있는 복사는 얕은 복사에 불과합니다.이 답변이 질문에 뭔가 추가됐으면 좋겠어요.
예
칩시다.[1, 2, 3]
. 할 수 레이어는 수 있습니다 불변의 숫자는 복제할 수 없지만 다른 계층은 복제할 수 있습니다. 목록할 수 .[x for 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, flast, ints, 심지어 목록까지 재귀를 사용하여 무한히 많은 계층에 딥 카피할 수 있는 기능이 있습니다.자, 여기 있네요, 딥카피.
TLDR: Deepcopy는 재귀를 사용하여 개체를 복제하고, 불변의 개체를 복제할 수 없으므로 이전과 동일한 불변의 개체를 반환합니다.그러나, 이것은 물체의 가장 바깥쪽에 있는 변이 가능한 층에 도달할 때까지 변이 가능한 물체의 가장 안쪽 층을 깊게 복사합니다.
는 Attribute를 경우가 .copy.copy()
★★★★★★★★★★★★★★★★★」copy.deepcopy()
3 "Python 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))
출력:
lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
Python에서는, 다음의 점에 주의해 주세요.
list1 = ['apples','bananas','pineapples']
list2 = list1
List2는 실제 목록을 저장하는 것이 아니라 list1에 대한 참조입니다.list1은on )을하여 목록의 복사본(copy module(download on pip))을 만듭니다.copy.copy()
리스트의 는, 「」를 참조해 주세요.copy.deepcopy()
네스트된 것에 대해서).그러면 첫 번째 목록과 변경되지 않는 복사본이 만들어집니다.
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)
또 빈 하는 방법: list까지른른른른른른른른른::::::::::::::::::::: there there there there there: there there there there there there there there there there there there.l2 = l + []
.
Python 3.8로 테스트했습니다.
l = [1,2,3]
l2 = l + []
print(l,l2)
l[0] = 'a'
print(l,l2)
최선의 답은 아니지만 효과가 있다.
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]]]
-----------------------------
행이 '''로 되어 때문입니다.new_list = my_list
는 변수 "예"에 새로운 참조를 합니다.my_list
, 「」입니다.new_list
은 '비슷하다'와.C
입니다.
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
복사 모듈을 사용하여 새 목록을 만들어야 합니다.
import copy
new_list = copy.deepcopy(my_list)
사용하는 방법은 복사할 목록의 내용에 따라 달라집니다.에 네스트된 " " 가 되어 있는 dicts
딥카피그렇지 않으면 응답에 기재되어 있는 대부분의 방식(복사, 루프 [for], 복사, 확장, 결합 또는 언팩)이 동시에 동작하여 실행됩니다(최악의 원인이 된 루프와 딥카피는 제외).
대본
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)
결과.
[{"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}
]}]
언급URL : https://stackoverflow.com/questions/2612802/how-do-i-clone-a-list-so-that-it-doesnt-change-unexpectedly-after-assignment
'IT' 카테고리의 다른 글
SQL: 프리픽스가 있는 테이블 삭제 (0) | 2022.12.27 |
---|---|
Python을 사용하여 touch를 구현하시겠습니까? (0) | 2022.12.27 |
JavaScript 파일 업로드 크기 확인 (0) | 2022.12.27 |
navigator.geolocation.getCurrentPosition이 작동하지 않을 수 있음 (0) | 2022.12.27 |
PHP의 MariaDB에서 정보 가져오기 (0) | 2022.12.27 |