답변:
글을 쓰면 [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로 바꿀 수 있습니다 .
[4]*3
본질적으로와 같습니다 x = 4; [x, x, x]
. 그러나 이것이 불변 이기 때문에 아무런 문제4
가 발생하지 않는다는 것은 사실입니다 . 또한 다른 예는 실제로 다른 경우가 아닙니다. a = [x]*3; a[0] = 5
경우에도 문제가 발생하지 않습니다 x
당신은 수정하지 않는 때문에, 변경 가능한 x
만 수정 a
. 나는 내 대답을 오도하거나 잘못된 것으로 묘사하지 않을 것입니다- 불변의 물건을 다루는 경우 발로 자신을 쏠 수 없습니다 .
x = 1000; lst = [x]*2; lst[0] is lst[1]
-> True
. 파이썬은 여기서 변경 가능한 객체와 변경 불가능한 객체를 구별하지 않습니다.
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]
x
. 당신이 세계적으로 독특한 물건을 x = object()
만들고 다음과 matrix = [[x] * 2]
같이 실현 matrix[0][0] is matrix[0][1]
list
) 그래서 만약 row = [x] * 2
(A)보다 matrix = [row] * 2
두 행이 정확히 같은 객체, 지금은 하나의 행으로 변경 곳이 matrix[0][0] = y
갑자기 다른 하나에 반영(matrix[0][0] is matrix[1][0]) == True
실제로 이것은 정확히 당신이 기대하는 것입니다. 여기서 일어나는 일을 분해 해 봅시다 :
당신은 쓰기
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에 대해 식을 다시 평가하여 다른 목록을 만듭니다.
id(lst[0][0])
및 id(lst[1][0])
심지어 나 id(lst[0])
하고id(lst[1])
[[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
obj[2] = 42
가 참조2
하는 객체를 변경하는 것과는 반대로 index 가 참조 를 대체합니다myList[2][0] = ...
( myList[2]
목록이며 어설 션은 인덱스가 0 인 참조를 목록에서 변경 함). 물론 정수는 변경할 수 없지만 많은 객체 유형 이 있습니다. 그리고 [....]
리스트 표시 표기법도 리터럴 구문의 한 형태입니다! 변경 가능 대 변경 불가능한 오브젝트와 복합 (예 : 목록) 및 스칼라 오브젝트 (예 : 정수)를 혼동하지 마십시오.
간단히 말해서 이것은 파이썬에서 모든 것이 참조로 작동하기 때문에 발생합니다 . 따라서 목록 목록을 만들면 기본적으로 그러한 문제가 발생합니다.
문제를 해결하려면 다음 중 하나를 수행하십시오. 1. numpy.empty에 대해 numpy 배열 설명서를 사용하십시오 . 2. 목록에 도달하면 목록을 추가하십시오. 3. 원하는 경우 사전을 사용할 수도 있습니다
다음과 같은 방법으로 코드를 다시 작성하십시오.
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
라는 새 목록을 얻는 것입니다. 즉, 초기 객체에 대한 참조 인 4 개의 요소가있는 새 목록을 만듭니다 . 순 단계는 매우 비슷합니다. 당신은 기본적으로 수행 되는, 반품 및 첫 번째 단계와 동일한 이유.z
[x, x, x, x]
x
z * 3
[[x, x, x, x]] * 3
[[x, x, x, x], [x, x, x, x], [x, x, x, x]]
보다 설명 적으로 설명하려고하면
작업 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 :
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]]
파이썬리스트 곱셈 에서 @spelchekr : [[...]] * 3는 수정 될 때 서로를 미러링하는 3 개의리스트를 만들고, "왜 외부 * 3만이 더 많은 참조를 생성하지만 내부는 그렇지 않습니까? "왜 모두 1이 아닌가?"
li = [0] * 3
print([id(v) for v in li]) # [140724141863728, 140724141863728, 140724141863728]
li[0] = 1
print([id(v) for v in li]) # [140724141863760, 140724141863728, 140724141863728]
print(id(0)) # 140724141863728
print(id(1)) # 140724141863760
print(li) # [1, 0, 0]
ma = [[0]*3] * 3 # mainly discuss inner & outer *3 here
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
ma[0][0] = 1
print([id(li) for li in ma]) # [1987013355080, 1987013355080, 1987013355080]
print(ma) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
위의 코드를 시도한 후의 설명은 다음과 같습니다.
*3
또한 참조를 만들지 만의 참조는 같은 불변 [&0, &0, &0]
변화 할 때 다음 li[0]
, 당신은 CONST INT의 기본 참조를 변경할 수 없습니다 0
그냥 새에 참조 주소를 변경할 수 있도록 &1
;ma=[&li, &li, &li]
과 li
, 변경 가능한 전화 할 때 너무 ma[0][0]=1
, 엄마 [0] [0] 동일하게하는 것입니다 &li[0]
모든 때문에, &li
인스턴스에 자사의 첫번째 주소가 변경됩니다 &1
.내장 목록 기능을 사용하면 다음과 같이 할 수 있습니다
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
a.insert(0,[5,1,1,1])
[x]*3
3 개의 참조를 저장하십시오 . 예를 들어이 does't 작업 , 어디 한 후 ,[x, x, x]
x
a=[4]*3
a[0]=5
a=[5,4,4].