목록 변경 사항 목록이 예기치 않게 하위 목록에 반영됨
Python으로 목록 목록을 만들어야했기 때문에 다음을 입력했습니다.
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]. 즉, 동일한 x. 이 싱글을 수정하면 x세 가지 참조를 통해 볼 수 있습니다.
이를 수정하려면 각 위치에 새 목록을 만들어야합니다. 한 가지 방법은
[[1]*4 for _ in range(3)]
[1]*4한 번 평가하고 1 개의 목록을 3 번 참조하는 대신 매번 재평가 합니다.
*목록 이해력이하는 방식으로 독립 개체를 만들 수없는 이유가 궁금 할 수 있습니다 . 곱셈 연산자 *가 표현식을 보지 않고 객체에서 작동 하기 때문 입니다. 를 사용 *하여 [[1] * 4]3 을 곱 하면 식 텍스트가 아니라 *요소 1 개 목록 만 [[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 = 21을 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 개가 아니라 내부 목록을 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()하는 경우 throwaway 변수 대신 더 효율적인 생성기를 반환하는 ( range()python 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])
간단히 말해서 이것은 파이썬에서 모든 것이 참조로 작동하기 때문에 발생하므로 목록 목록을 만들면 기본적으로 이러한 문제가 발생합니다.
문제를 해결하려면 다음 중 하나를 수행 할 수 있습니다. 1. numpy.empty에 대한 numpy 배열 문서를 사용합니다. 2. 목록에 도달하면 목록을 추가합니다. 3. 원하는 경우 사전을 사용할 수도 있습니다.
Python 컨테이너에는 다른 개체에 대한 참조가 포함되어 있습니다. 이 예를 참조하십시오.
>>> 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)]
여기서 주목해야 할 중요한 점 은 *연산자가 대부분 리터럴 목록 을 만드는 데 사용된다는 것 입니다. 이후 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를 인쇄하는 것 입니다.
객체의 "ID"를 반환합니다.
이를 식별하고 어떤 일이 발생하는지 분석하는 데 도움이됩니다.
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
So now let us go step-by-step. You have x which is 1, and a single element list y containing x. Your first step is y * 4 which will get you a new list z, which is basically [x, x, x, x], i.e. it creates a new list which will have 4 elements, which are references to the initial x object. The net step is pretty similar. You basically do z * 3, which is [[x, x, x, x]] * 3 and returns [[x, x, x, x], [x, x, x, x], [x, x, x, x]], for the same reason as for the first step.
I guess everybody explain what is happening. I suggest one way to solve it:
myList = [[1 for i in range(4)] for j in range(3)]
myList[0][0] = 5
print myList
And then you have:
[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Trying to explain it more descriptively,
Operation 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]]
Operation 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]]
Noticed why doesn't modifying the first element of the first list didn't modify the second element of each list? That's because [0] * 2 really is a list of two numbers, and a reference to 0 cannot be modified.
If you want to create clone copies, try Operation 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]]
another interesting way to create clone copies, Operation 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]]
By using the inbuilt list function you can do like this
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
@spelchekr from Python list multiplication: [[...]]*3 makes 3 lists which mirror each other when modified and I had the same question about "Why does only the outer *3 create more references while the inner one doesn't? Why isn't it all 1s?"
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]]
Here is my explanation after trying the code above:
- 내부
*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.
'Programing' 카테고리의 다른 글
| Vim에서 커서를 이동하지 않고 화면을 이동하는 방법은 무엇입니까? (0) | 2020.10.04 |
|---|---|
| 생성 알고리즘과 차별 알고리즘의 차이점은 무엇입니까? (0) | 2020.10.04 |
| Node.js와 함께 jQuery를 사용할 수 있습니까? (0) | 2020.10.04 |
| .Any () 대 .Count ()> 0 중 어떤 방법이 더 잘 수행됩니까? (0) | 2020.10.04 |
| 클래스의 속성 목록을 얻는 방법은 무엇입니까? (0) | 2020.10.04 |