목록의 목록은 예기치 않게 하위 목록에 반영된 변경 사항입니다
-
04-07-2019 - |
문제
파이썬에서 목록 목록을 만들어야 했으므로 다음을 입력했습니다.
myList = [[1] * 4] * 3
목록은 다음과 같습니다.
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
그런 다음 가장 안쪽 값 중 하나를 변경했습니다.
myList[0][0] = 5
이제 내 목록은 다음과 같습니다.
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
내가 원하거나 기대했던 것이 아닙니다. 누군가 무슨 일이 일어나고 있는지, 어떻게 돌아 다니는 지 설명해 주시겠습니까?
해결책
당신이 쓸 때 [x]*3
당신은 본질적으로 목록을 얻습니다 [x, x, x]
. 즉, 동일하게 3 개의 참조가있는 목록 x
. 그런 다음이 싱글을 수정하십시오 x
그것은 세 가지 참조를 통해 볼 수 있습니다.
이를 해결하려면 각 위치에서 새 목록을 작성해야합니다. 그것을하는 한 가지 방법입니다
[[1]*4 for _ in range(3)]
재평가 할 것입니다 [1]*4
매번 한 번 평가하고 1 목록에 3 개의 참조를 작성하는 대신 매번.
왜 그런지 궁금 할 것입니다 *
목록 이해력이있는 방식으로 독립적 인 대상을 만들 수는 없습니다. 곱셈 연산자 때문입니다 *
표현을 보지 않고 개체에서 작동합니다. 당신이 사용할 때 *
번식하다 [[1] * 4]
3, *
1 요소 목록 만 볼 수 있습니다 [[1] * 4]
가 아니라 평가합니다 [[1] * 4
표현식 텍스트. *
그 요소의 사본을 만드는 방법, 재평가 방법을 모른다 [[1] * 4]
, 그리고 당신이 사본을 원한다는 것을 알지 못하며 일반적으로 요소를 복사 할 방법조차 없을 수도 있습니다.
유일한 옵션 *
Has는 새로운 하위 목록을 만드는 대신 기존 하위 목록을 새로운 언급하는 것입니다. 다른 것은 일관성이 없거나 기본 언어 설계 결정의 주요 재 설계가 필요합니다.
대조적으로, 목록 이해력은 모든 반복에서 요소 표현식을 재평가합니다. [[1] * 4 for n in range(3)]
재평가 [1] * 4
같은 이유로 매번 [x**2 for x in range(3)]
재평가 x**2
매번. 모든 평가 [1] * 4
새 목록을 생성하므로 목록 이해력이 원하는 것을 수행합니다.
우연히 [1] * 4
또한 요소를 복사하지 않습니다 [1]
, 그러나 정수는 불변이기 때문에 그것은 중요하지 않습니다. 당신은 같은 일을 할 수 없습니다 1.value = 2
그리고 1을 2로 바꿉니다.
다른 팁
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]
사실, 이것은 정확히 당신이 기대하는 것입니다. 여기서 일어나는 일을 분해합시다.
당신은 쓰기
lst = [[1] * 4] * 3
이것은 다음과 같습니다.
lst1 = [1]*4
lst = [lst1]*3
이것은 의미합니다 lst
3 개의 요소가 모두 가리키는 목록입니다 lst1
. 이것은 다음 두 줄이 동일하다는 것을 의미합니다.
lst[0][0] = 5
lst1[0] = 5
처럼 lst[0]
아무것도 아닙니다 lst1
.
원하는 동작을 얻으려면 목록 이해력을 사용할 수 있습니다.
lst = [ [1]*4 for n in xrange(3) ]
이 경우, 표현식은 각 N에 대해 재평가되어 다른 목록을 초래합니다.
[[1] * 4] * 3
또는:
[[1, 1, 1, 1]] * 3
내부를 참조하는 목록을 만듭니다 [1,1,1,1]
3 번 - 내부 목록의 세 가지 사본이 아니므로 목록을 수정할 때마다 (모든 위치에서) 변경 사항이 세 번 나타납니다.
이 예와 동일합니다.
>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]
아마도 조금 놀랍지 않을 것입니다.
Python-2.x 사용을 사용하는 경우, 목록 이해 내에서 문제를 올바르게 설명하는 수용된 답변과 함께 xrange()
더 효율적인 발전기를 반환합니다 (range()
Python 3에서 동일한 작업을 수행합니다) _
Throwaway 변수 대신 n
:
[[1]*4 for _ in xrange(3)] # and in python3 [[1]*4 for _ in range(3)]
또한 훨씬 더 피티닉 사용할 수있는 방법 itertools.repeat()
반복 된 요소의 반복자 객체를 만들려면 :
>>> a=list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0]=5
>>> a
[5, 1, 1, 1]
추신 : Numpy를 사용하면, 당신이 사용할 수있는 것만 만들고 싶다면 사용할 수 있습니다. np.ones
그리고 np.zeros
및/또는 다른 번호 사용 np.repeat()
:
In [1]: import numpy as np
In [2]:
In [2]: np.ones(4)
Out[2]: array([ 1., 1., 1., 1.])
In [3]: np.ones((4, 2))
Out[3]:
array([[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.]])
In [4]: np.zeros((4, 2))
Out[4]:
array([[ 0., 0.],
[ 0., 0.],
[ 0., 0.],
[ 0., 0.]])
In [5]: np.repeat([7], 10)
Out[5]: array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
간단한 말로 이것은 파이썬에서 모든 것이 작동하기 때문에 일어나고 있습니다. 참조로, 따라서 목록 목록을 이렇게 만들 때 기본적으로 그러한 문제가 발생합니다.
문제를 해결하려면 문제 중 하나를 수행 할 수 있습니다. 1. Numpy Array를 사용하십시오. numpy.empty에 대한 문서2. 목록에 도달하면 목록을 추가하십시오. 3. 원하는 경우 사전을 사용할 수도 있습니다
파이썬 컨테이너에는 다른 물체에 대한 참조가 포함되어 있습니다. 이 예를 참조하십시오 :
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
이것에서 b
목록에 대한 참조 인 하나의 항목을 포함하는 목록입니다. a
. 목록 a
변이 가능합니다.
정수에 의한 목록의 곱셈은 목록을 여러 번 추가하는 것과 같습니다 ( 일반적인 시퀀스 작업). 따라서 예를 계속 유지합니다.
>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]
목록을 볼 수 있습니다 c
이제 목록에 대한 두 가지 참조가 포함되어 있습니다 a
이는 동등합니다 c = b * 2
.
Python FAQ는 또한이 동작에 대한 설명도 포함합니다. 다차원 목록을 어떻게 만들려면?
myList = [[1]*4] * 3
하나의 목록 개체를 만듭니다 [1,1,1,1]
메모리에서 참조를 3 배 이상 복사합니다. 이것은 동일합니다 obj = [1,1,1,1]; myList = [obj]*3
. 모든 수정 obj
어디에서나 세 곳에 반영됩니다 obj
목록에 참조됩니다. 올바른 진술은 다음과 같습니다.
myList = [[1]*4 for _ in range(3)]
또는
myList = [[1 for __ in range(4)] for _ in range(3)]
여기에 주목해야 할 중요한 사항 그게 다 *
운영자입니다 주로 a를 만드는 데 사용됩니다 리터럴 목록. 부터 1
문자 그대로이므로 obj =[1]*4
만들 것입니다 [1,1,1,1]
각각 1
원자체이고 ~ 아니다 의 참조 1
4 번 반복. 이것은 우리가한다면 의미합니다 obj[2]=42
, 그 다음에 obj
될 것입니다 [1,1,42,1]
~ 아니다 일부는 가정 할 수 있습니다.[42,42,42,42]
다음과 같은 방식으로 코드를 다시 작성하겠습니다.
x = 1
y = [x]
z = y * 4
myList = [z] * 3
그런 다음 다음 코드를 실행하여 모든 것을 더 명확하게 만듭니다. 코드가하는 일은 기본적으로 인쇄하는 것입니다 id
얻은 물체의 s
객체의“정체성”을 반환하십시오
그리고 우리가 그것들을 식별하고 어떤 일이 일어나는지 분석하는 데 도움이 될 것입니다.
print("myList:")
for i, subList in enumerate(myList):
print("\t[{}]: {}".format(i, id(subList)))
for j, elem in enumerate(subList):
print("\t\t[{}]: {}".format(j, id(elem)))
다음과 같은 출력을 얻을 수 있습니다.
x: 1
y: [1]
z: [1, 1, 1, 1]
myList:
[0]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
[1]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
[2]: 4300763792
[0]: 4298171528
[1]: 4298171528
[2]: 4298171528
[3]: 4298171528
이제 단계별로 가자. 당신은 가지고 있습니다 x
그게 1
, 및 단일 요소 목록 y
포함 x
. 첫 번째 단계는입니다 y * 4
새로운 목록을 얻을 수 있습니다 z
, 기본적으로 [x, x, x, x]
, 즉, 4 개의 요소가있는 새 목록을 만듭니다. x
물체. 순 단계는 꽤 비슷합니다. 당신은 기본적으로합니다 z * 3
, 그것은 [[x, x, x, x]] * 3
그리고 반환 [[x, x, x, x], [x, x, x, x], [x, x, x, x]]
, 첫 번째 단계와 같은 이유로.
모두가 무슨 일이 일어나고 있는지 설명하는 것 같아요. 나는 그것을 해결하는 한 가지 방법을 제안합니다.
myList = [[1 for i in range(4)] for j in range(3)]
myList[0][0] = 5
print myList
그리고 당신은 다음과 같습니다.
[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
더 설명 적으로 설명하려고 노력하고
작동 1 : :
x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]
x[0][0] = 1
print(x) # [[1, 0], [0, 0]]
작동 2 : : : : : : 작동 2 :
y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]
y[0][0] = 1
print(y) # [[1, 0], [1, 0]]
첫 번째 목록의 첫 번째 요소를 수정하지 않는 이유가 각 목록의 두 번째 요소를 수정하지 않았습니까? 그것은 ~ 때문에 [0] * 2
실제로 두 숫자 목록이며 0에 대한 참조는 수정할 수 없습니다.
클론 사본을 만들려면 작동 3을 시도해보십시오.
import copy
y = [0] * 2
print(y) # [0, 0]
y = [y, copy.deepcopy(y)]
print(y) # [[0, 0], [0, 0]]
y[0][0] = 1
print(y) # [[1, 0], [0, 0]]
클론 사본을 만드는 또 다른 흥미로운 방법, 작동 4 :
import copy
y = [0] * 2
print(y) # [0, 0]
y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]
y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]
내장 목록 기능을 사용하면 다음과 같이 할 수 있습니다.
a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list
a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number
a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting
a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list