programing

하위 목록에 예기치 않게 반영된 목록 변경 사항 목록

coolbiz 2023. 1. 17. 21:42
반응형

하위 목록에 예기치 않게 반영된 목록 변경 사항 목록

목록 목록을 만들었습니다.

xs = [[1] * 4] * 3

# xs == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]  

그런 다음 가장 안쪽 값 중 하나를 변경했습니다.

xs[0][0] = 5

# xs == [[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]  

첫가 모두 로 입니까?5

쓸 때[x]*3 '목록이 생깁니다.[x, x, x] ,, 개, 개, 개, 개, 개, 개, 개, 개, 개, 개, 개, 개, 개, ., 개, ., ., ., ., ., ., ..xx다음 세 가지 참조를 통해 모두 볼 수 있습니다.

x = [1] * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
    f"id(xs[0]): {id(xs[0])}\n"
    f"id(xs[1]): {id(xs[1])}\n"
    f"id(xs[2]): {id(xs[2])}"
)
# id(xs[0]): 140560897920048
# id(xs[1]): 140560897920048
# id(xs[2]): 140560897920048

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"xs: {xs}")
# xs: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

이 문제를 해결하려면 각 위치에 새 목록을 작성해야 합니다.한 가지 방법은

[[1]*4 for _ in range(3)]

[1]*4한 번 평가하고 한 개의 목록을 3개 참조하는 대신 매번.


그랬는지 수도 요.*목록 이해 방식으로는 독립 객체를 만들 수 없습니다. 연산자이기 입니다.*는 표현식을 표시하지 않고 오브젝트에 대해 동작합니다.「 」를 사용하는 *[[1] * 4] 셋, 셋, 넷*의 리스트가 됩니다.[[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 . 의의uation의 모든 [1] * 4새 목록이 생성되므로 목록 이해는 원하는 대로 수행됩니다.

★★★★★★★★★★★★★★★★.[1] * 4 이 .[1]하지만 정수는 불변의 것이기 때문에 그건 문제가 되지 않는다.런런 you' 、 、 어 、 you 、 you 、 you 、 you 、 you 、 。1.value = 2을 2 2인치로 합니다.

size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for _ in range(size)]

Python Tutor를 사용한 실시간 시각화:

프레임과 오브젝트

사실, 이게 바로 네가 예상한 거야.여기서 무슨 일이 일어나고 있는지 분해해 봅시다.

당신이 글을 쓰세요

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) ]

이 은 각각에 됩니다.n하다

[[1] * 4] * 3

또는 다음과 같은 경우도 있습니다.

[[1, 1, 1, 1]] * 3

하는 리스트를 합니다.[1,1,1,1]내부 목록의 복사본이 세 개가 아니라 세 번이므로, 목록을 수정할 때마다(어느 위치에서든) 변경 내용이 세 번 표시됩니다.

다음 예시와 같습니다.

>>> 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]]

별로 놀랍지 않은 곳이죠

my_list = [[1]*4] * 3 1을 .[1,1,1,1]3번은 this은은 this this this this this this this this this에 해당합니다.obj = [1,1,1,1]; my_list = [obj]*3에 수정 사항.obj에 반영됩니다.obj을 사용하다올바른 설명은 다음과 같습니다.

my_list = [[1]*4 for _ in range(3)]

또는

my_list = [[1 for __ in range(4)] for _ in range(3)]

여기서 주의할 점은,*연산자는 대부분 리터럴 목록을 만드는 데 사용됩니다.비록 ~일지라도1 불변, 불변, 불변, 불변, 불변, 불변, 불변.obj = [1]*4에서는, 됩니다.1하여 4회 형성하다[1,1,1,1]그러나 불변의 객체에 대한 참조가 이루어지면 객체는 새 객체로 덮어씁니다.

, ' 하면'이라는입니다.obj[1] = 42 , , , 「 」obj될 것이다[1,42,1,1] 하지 않다 짐작할 수 있듯이이것은, 다음과 같이 검증할 수도 있습니다.

>>> my_list = [1]*4
>>> my_list
[1, 1, 1, 1]

>>> id(my_list[0])
4522139440
>>> id(my_list[1])  # Same as my_list[0]
4522139440

>>> my_list[1] = 42  # Since my_list[1] is immutable, this operation overwrites my_list[1] with a new object changing its id.
>>> my_list
[1, 42, 1, 1]

>>> id(my_list[0])
4522139440
>>> id(my_list[1])  # id changed
4522140752
>>> id(my_list[2])  # id still same as my_list[0], still referring to value `1`.
4522139440

다음 코드를 사용하여 중복된 요소를 포함하는 목록을 작성하는 대신 문제를 올바르게 설명한 승인된 답변과 함께:

[[1]*4 for _ in range(3)]

또한 를 사용하여 반복 요소의 반복기 개체를 만들 수도 있습니다.

>>> a = list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0] = 5
>>> a
[5, 1, 1, 1]

추신: NumPy를 사용하고 있으며 사용할 수 있는 1 또는 0 배열만 작성하려는 경우 및/또는 다른 숫자에 사용할 수 있습니다.

>>> import numpy as np
>>> np.ones(4)
array([1., 1., 1., 1.])
>>> np.ones((4, 2))
array([[1., 1.],
       [1., 1.],
       [1., 1.],
       [1., 1.]])
>>> np.zeros((4, 2))
array([[0., 0.],
       [0., 0.],
       [0., 0.],
       [0., 0.]])
>>> np.repeat([7], 10)
array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])

Python 컨테이너에는 다른 개체에 대한 참조가 포함되어 있습니다.다음의 예를 참조해 주세요.

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

이점 this this에서는요.b는 목록 입니다.a ★★★★a가변성이 있습니다.

목록에 정수를 곱하는 것은 목록에 여러 번 추가하는 것과 같습니다(일반 시퀀스 연산 참조).다음으로 예를 제시하겠습니다.

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

는 「」라고 하는 것을 알 수.c에는 2개의 되어 있습니다.a은 와와다다 which에 해당합니다.c = b * 2.

Python FAQ에는 다음 동작에 대한 설명도 포함되어 있습니다.다차원 목록을 작성하려면 어떻게 해야 합니까?

다음의 방법으로 코드를 고쳐 씁니다.

x = 1
y = [x]
z = y * 4

my_list = [z] * 3

그런 다음 다음 다음 코드를 실행하여 모든 것을 명확히 합니다.코드가 하는 일은 기본적으로 취득한 오브젝트의 s를 인쇄하는 것입니다.

개체의 "아이덴티티"를 반환합니다.

이러한 정보를 식별하고 어떤 일이 일어나는지 분석하는 데 도움이 됩니다.

print("my_list:")
for i, sub_list in enumerate(my_list):
    print("\t[{}]: {}".format(i, id(sub_list)))
    for j, elem in enumerate(sub_list):
        print("\t\t[{}]: {}".format(j, id(elem)))

다음과 같은 출력이 표시됩니다.

x: 1
y: [1]
z: [1, 1, 1, 1]
my_list:
    [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 목록 " " " "yxy * 4 새로운 이 나옵니다.z으로 「」입니다).[x, x, x, x]의 4가지 요소,즉 4가지 이것들은 첫 번째에 대한 참조입니다.x, 다음 는 비슷해요하다으로는 그렇게 해요.z * 3, 「」)[[x, x, x, x]] * 3를 반환한다.[[x, x, x, x], [x, x, x, x], [x, x, x, x]]네.

저는 같은 도표를 설명하기 위해 답변을 추가합니다.

2D를 만든 방법으로 얕은 목록을 만듭니다.

arr = [[0]*cols]*row

대신 목록의 요소를 업데이트하려면

rows, cols = (5, 5) 
arr = [[0 for i in range(cols)] for j in range(rows)] 

설명:

다음을 사용하여 목록을 만들 수 있습니다.

arr = [0]*N 

또는

arr = [0 for i in range(N)] 

첫 번째 경우 배열의 모든 인덱스가 동일한 정수 개체를 가리킵니다.

여기에 이미지 설명 입력

인덱스에 를 들어 int 객체가 생성됩니다.arr[4] = 5

여기에 이미지 설명 입력

목록을 작성하면 어떻게 되는지 봅시다.이 경우 상위 목록의 모든 요소가 동일한 목록을 가리킵니다.

여기에 이미지 설명 입력

인덱스의 값을 갱신하면 새로운 int 객체가 생성됩니다.그러나 모든 최상위 목록 인덱스가 동일한 목록을 가리키므로 모든 행이 동일하게 표시됩니다.요소를 업데이트하면 해당 열의 모든 요소가 업데이트된다는 느낌을 받을 수 있습니다.

여기에 이미지 설명 입력

크레딧:Pranav Devarakonda의 간단한 설명에 감사드립니다.

간단히 말하면 파이썬에서는 모든 것이 참조로 동작하기 때문에 기본적으로 이러한 문제가 발생합니다.

문제를 해결하려면 다음 중 하나를 수행합니다.1 .numpy.empty 2에는 numpy 어레이 매뉴얼을 사용합니다.리스트가 뜨는 대로 리스트를 추가하세요.3 .원하는 경우 사전을 사용할 수도 있습니다.

모두가 무슨 일이 일어나고 있는지 설명하고 있다.이 문제를 해결할 수 있는 한 가지 방법을 제안합니다.

my_list = [[1 for i in range(4)] for j in range(3)]

my_list[0][0] = 5
print(my_list)

그 후, 다음과 같이 됩니다.

[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]

@spelchekr from Python 목록 곱셈: [[...]*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] int의 기본 는 변경할 수 0를 새 &1;
  • 동시에ma = [&li, &li, &li] ★★★★★★★★★★★★★★★★★」li, 「」를 하면,ma[0][0] = 1,ma[0][0] &li[0] "" " " " " "&li는 첫 번째 를 """로 합니다.&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:

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는 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

제가 여기 온 이유는 어떻게 하면 임의의 수의 목록을 모을 수 있을지 궁금했기 때문입니다.위에는 많은 설명과 구체적인 예가 있지만, 다음과 같은 재귀 함수를 사용하여 ...의 목록 N차원 목록을 일반화할 수 있습니다.

import copy

def list_ndim(dim, el=None, init=None):
    if init is None:
        init = el

    if len(dim)> 1:
        return list_ndim(dim[0:-1], None, [copy.copy(init) for x in range(dim[-1])])

    return [copy.deepcopy(init) for x in range(dim[0])]

다음과 같이 함수에 첫 번째 호출을 다음과 같이 합니다.

dim = (3,5,2)
el = 1.0
l = list_ndim(dim, el)

서 ''는(3,5,2)구조 치수의 튜플입니다(numpy와 유사).shape 및 argument)를 합니다.1.0는 구조체를 초기화하는 요소입니다(없음과도 함께 동작합니다).에 주의:init된 자녀 에서만 제공됩니다.

상기의 출력:

[[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]]]

특정 요소 설정:

l[1][3][1] = 56
l[2][2][0] = 36.0+0.0j
l[0][1][0] = 'abc'

결과 출력:

[[[1.0, 1.0], ['abc', 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 56.0], [1.0, 1.0]],
 [[1.0, 1.0], [1.0, 1.0], [(36+0j), 1.0], [1.0, 1.0], [1.0, 1.0]]]

리스트의 비고유성은 위에서 설명된다

원문에서는 곱셈 연산자로 서브리스트를 구성했지만, 서브리스트에 같은 리스트를 사용하는 예를 추가하겠습니다.이 질문은 문제의 표준으로 사용되는 경우가 많기 때문에 이 답변을 추가하여 완성도를 높입니다.

node_count = 4
colors = [0,1,2,3]
sol_dict = {node:colors for node in range(0,node_count)}

각 사전 값의 목록이 동일한 개체이므로 사전 값 중 하나를 변경하려고 하면 모두 표시됩니다.

>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> [v is colors for v in sol_dict.values()]
[True, True, True, True]
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 2, 3], 2: [0, 2, 3], 3: [0, 2, 3]}

사전을 구성하는 올바른 방법은 각 값에 대해 목록의 복사본을 사용하는 것입니다.

>>> colors = [0,1,2,3]
>>> sol_dict = {node:colors[:] for node in range(0,node_count)}
>>> sol_dict
{0: [0, 1, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}
>>> sol_dict[0].remove(1)
>>> sol_dict
{0: [0, 2, 3], 1: [0, 1, 2, 3], 2: [0, 1, 2, 3], 3: [0, 1, 2, 3]}

시퀀스의 항목은 복사되지 않고 여러참조됩니다.이것은 새로운 Python 프로그래머에게 자주 나타난다.

>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

일이 요, ★★★★★★★★★★★★★★★★★★★★★★★★★★」[[]]는 빈에, 「1」의 3개의 「3」입니다.[[]] * 3아, 네, 네, 네.목록의 요소 중 하나를 변경하면 이 단일 목록이 변경됩니다.

이를 설명하는 또 다른 예는 다차원 배열을 사용하는 것입니다.

다음과 같은 다차원 배열을 만들려고 했을 것입니다.

>>> A = [[None] * 2] * 3

인쇄하면 다음과 같이 표시됩니다.

>>> A
[[None, None], [None, None], [None, None]]

그러나 값을 할당하면 값은 여러 위치에 표시됩니다.

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

는 목록 가 ''로 되어 있기 입니다.*는 복사본을 만들지 않고 기존 개체에 대한 참조만 생성합니다.3은 길이 2의 동일한 목록에 대한 3개의 참조를 포함하는 목록을 만듭니다.한 행에 대한 변경 내용은 모든 행에 표시되며, 이는 원하는 행이 아닙니다.

언급URL : https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly

반응형