파이썬으로 목록 목록을 만들어야했기 때문에 다음과 같이 입력했습니다.
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
세 개의 모든 참조를 통해 볼 수 있습니다.
x = [1] * 4
l = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(l[0]): {id(l[0])}\n"
f"id(l[1]): {id(l[1])}\n"
f"id(l[2]): {id(l[2])}"
)
# id(l[0]): 140560897920048
# id(l[1]): 140560897920048
# id(l[2]): 140560897920048
x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"l: {l}")
# l: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]
수정하려면 각 위치에 새 목록을 작성해야합니다. 그것을하는 한 가지 방법은
[[1]*4 for _ in range(3)]
[1]*4
한 번 평가하지 않고 매번 1 개의 목록을 참조하는 대신 매번 재평가 합니다.
왜 *
목록 이해와 같은 방식으로 독립 객체를 만들 수 없는지 궁금 할 것입니다. 곱셈 연산자 *
는 표현식을 보지 않고 객체에서 작동 하기 때문 입니다. 3 *
을 곱하는 데 사용 하면 식 텍스트가 아닌 1 요소 목록 만 평가됩니다 . 해당 요소의 사본을 만드는 방법 , 재평가 방법, 모방조차 원치 않으며 일반적으로 요소를 복사 할 수있는 방법이 없을 수도 있습니다.[[1] * 4]
*
[[1] * 4]
[[1] * 4
*
[[1] * 4]
유일한 옵션 *
은 새 하위 목록을 만들지 않고 기존 하위 목록을 새로 참조하는 것입니다. 다른 언어가 일관성이 없거나 기본 언어 설계 결정을 크게 재 설계해야합니다.
반대로, 목록 이해는 모든 반복에서 요소 표현식을 재평가합니다. [[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 range(3) ] #python 3
lst = [ [1]*4 for n in xrange(3) ] #python 2
이 경우 각 n에 대해 식을 다시 평가하여 다른 목록을 만듭니다.
답변
[[1] * 4] * 3
또는:
[[1, 1, 1, 1]] * 3
[1,1,1,1]
내부 목록의 3 개 사본이 아닌 내부를 3 번 참조하는 목록을 작성하므로 목록을 수정하면 (어느 위치에서든) 변경 사항이 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()
하는 경우 python-2.x를 사용 하는 경우 끔찍한 변수 대신 보다 효율적인 ( range()
파이썬 3에서 동일한 작업을 수행하는) 생성기를 반환하는 목록 이해 내에서 문제를 올바르게 설명하는 허용 된 답변과 함께 :_
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]
당신이 유일한 사람 또는 제로 사용할 수의 배열을 만들려면 PS는 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])
답변
파이썬 컨테이너는 다른 객체에 대한 참조를 포함합니다. 이 예제를보십시오 :
>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]
여기 b
에는 list에 대한 참조 인 하나의 항목이 포함 된 목록이 있습니다 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)]
여기서주의 할 점 은 *
연산자가 리터럴 목록 을 작성하는 데 주로 사용된다는 것 입니다. 불변 이지만 , 4 번 반복 하여 목록을 생성합니다 . 그러나 변경할 수없는 객체에 대한 참조가 있으면 해당 객체를 새 객체로 덮어 씁니다.1
obj =[1]*4
1
[1,1,1,1]
이것은 우리가 그렇게한다면 obj[1]=42
, 일부 사람들이 obj
예상 한대로 [1,42,1,1]
되지 않을 것임을 의미합니다 . 이것은 또한 확인할 수 있습니다 :[42,42,42,42]
>>> myList = [1]*4
>>> myList
[1, 1, 1, 1]
>>> id(myList[0])
4522139440
>>> id(myList[1]) # Same as myList[0]
4522139440
>>> myList[1] = 42 # Since myList[1] is immutable, this operation overwrites myList[1] with a new object changing its id.
>>> myList
[1, 42, 1, 1]
>>> id(myList[0])
4522139440
>>> id(myList[1]) # id changed
4522140752
>>> id(myList[2]) # id still same as myList[0], still referring to value `1`.
4522139440