복붙노트

[PYTHON] 곱셈 (*)을 사용하여 하위 목록 생성 예기치 않은 동작 [복제]

PYTHON

곱셈 (*)을 사용하여 하위 목록 생성 예기치 않은 동작 [복제]

나는 이것이 어딘가에 대답되어 졌다고 확신하지만 그것을 설명하는 방법을 모르겠습니다.

다음과 같이 3 개의 빈 목록을 포함하는 목록을 만들고 싶다고 가정 해 봅시다.

lst = [[], [], []]

나는이 일을함으로써 내가 영리하다고 생각했다.

lst = [[]] * 3

그러나 이상한 행동을 디버깅 한 결과 lst [0] .append (3)와 같이 전체 목록을 업데이트하고 [3], [3], [3 ]] 대신 [[3], [], []].

그러나 목록을 초기화하는 경우

lst = [[] for i in range(3)]

lst [1] .append (5)를 수행하면 예상되는 [[], [5], []]

내 질문은 왜 이것이 일어나는가? 흥미로운 점은 내가

lst = [[]]*3
lst[0] = [5]
lst[0].append(3)

셀 0의 '연결'이 깨져서 [[5,3], [], []]가 나오지만 lst [1] .append (0)는 [[5,3], [0] [0].

내 생각에 [[]] * x 형식으로 곱셈을 사용하면 파이썬이 단일 셀에 대한 참조를 저장하게됩니다 ...?

해결법

  1. ==============================

    1.예. 그리고 직접 테스트 할 수 있습니다.

    예. 그리고 직접 테스트 할 수 있습니다.

    >>> lst = [[]] * 3
    >>> print [id(x) for x in lst]
    [11124864, 11124864, 11124864]
    

    이것은 세 참조 모두가 동일한 객체를 참조 함을 나타냅니다. 그리고 이것이 실제로 발생한다는 것을 완벽하게 이해합니다 .1 단지 값을 복사하고,이 경우 값은 참조입니다. 그래서 같은 참조가 세 번 반복되는 것을 보았습니다.

    lst = [[]]*3
    lst[0] = [5]
    lst[0].append(3)
    

    lst [0]을 (를) 차지하는 참조를 변경했습니다. 즉, lst [0]에 새 값을 할당했습니다. 그러나 당신은 다른 요소들의 가치를 변화시키지 않았고, 여전히 그들이 언급 한 것과 같은 대상을 참조합니다. 그리고 lst [1]와 lst [2]는 여전히 정확히 같은 인스턴스를 참조하므로 물론 lst [1]에 항목을 추가하면 lst [2]도 해당 변경 사항을 볼 수 있습니다.

    이것은 사람들이 포인터와 참조로 만드는 고전적인 실수입니다. 여기에 간단한 비유가 있습니다. 너는 종이 조각이있다. 그 위에 누군가의 집 주소를 써야합니다. 당신은 이제 그 종이 조각을 가져다가 두 번 복사하여 똑같은 주소가 쓰여있는 종이 세 장으로 끝납니다. 이제 첫 번째 종이를 가져다 쓰고 주소를 쓰다듬고 다른 주소지에 새 주소를 적습니다. 다른 두 종이에 적힌 주소가 바뀌 었습니까? 그게 바로 당신의 코드가 한 일입니다. 그래서 다른 두 항목은 변경되지 않습니다. 또한 두 번째 종이에 주소가있는 집 소유자가 자신의 집에 추가 주차장을 설치했다고 가정 해보십시오. 이제 제가 물어 봅니다. 주소가 세 번째 종이에있는 집에 추가 차고가 있습니까? 예, 주소가 두 번째 종이에 쓰여진 집과 정확히 같은 집이기 때문에 그렇습니다. 이것은 두 번째 코드 예제에 대한 모든 것을 설명합니다.

    1 : 파이썬이 "복사 생성자"를 호출 할 것이라고 기대하지 않았습니까? 구토.

  2. ==============================

    2.이는 시퀀스 곱셈이 단순히 참조를 반복하기 때문입니다. [[]] * 2를 쓸 때 두 개의 요소로 된 새로운 목록을 만들지 만,이 두 요소는 모두 메모리에있는 동일한 객체, 즉 빈 목록입니다. 따라서 하나의 변화는 다른 하나의 변화에 ​​반영됩니다. 반면에 이해력은 각 반복마다 새로운 독립적 인 목록을 만듭니다.

    이는 시퀀스 곱셈이 단순히 참조를 반복하기 때문입니다. [[]] * 2를 쓸 때 두 개의 요소로 된 새로운 목록을 만들지 만,이 두 요소는 모두 메모리에있는 동일한 객체, 즉 빈 목록입니다. 따라서 하나의 변화는 다른 하나의 변화에 ​​반영됩니다. 반면에 이해력은 각 반복마다 새로운 독립적 인 목록을 만듭니다.

    >>> l1 = [[]] * 2
    >>> l2 = [[] for _ in xrange(2)]
    >>> l1[0] is l1[1]
    True
    >>> l2[0] is l2[1]
    False
    
  3. ==============================

    3.그들은 같은 목록을 참조하고 있습니다.

    그들은 같은 목록을 참조하고 있습니다.

    여기와 여기에 비슷한 질문이 있습니다.

    그리고 FAQ에서 :

  4. ==============================

    4.여러분은 [[]] * x 형태로 곱셈을 사용하면 파이썬이 단일 셀에 대한 참조를 저장하게된다는 생각이 맞습니다.

    여러분은 [[]] * x 형태로 곱셈을 사용하면 파이썬이 단일 셀에 대한 참조를 저장하게된다는 생각이 맞습니다.

    따라서 같은 목록에 대한 3 가지 참조 목록이 생깁니다.

  5. ==============================

    5.기본적으로 첫 번째 예제에서 일어나는 일은 목록이 동일한 내부 목록에 대한 여러 참조로 만들어지고 있다는 것입니다. 여기에 고장이 있습니다.

    기본적으로 첫 번째 예제에서 일어나는 일은 목록이 동일한 내부 목록에 대한 여러 참조로 만들어지고 있다는 것입니다. 여기에 고장이 있습니다.

    >>> a = []
    >>> b = [a]
    >>> c = b * 3  # c now contains three references to a
    >>> d = [ a for _ in xrange(4) ]  # and d contains four references to a
    >>> print c
    [[], [], []]
    >>> print d
    [[], [], [], []]
    >>> a.append(3)
    >>> print c
    [[3], [3], [3]]
    >>> print d
    [[3], [3], [3], [3]]
    >>> x = [[]] * 3  # shorthand equivalent to c
    >>> print x
    [[], [], []]
    >>> x[0].append(3)
    >>> print x
    [[3], [3], [3]]
    

    위의 내용은 첫 번째 예와 동일합니다. 이제 각 목록에 자체 변수가 주어 졌으므로 잘하면 왜 더 명확한 지 알 수 있습니다. 두 표현식이 같은 객체 (a)로 평가되기 때문에 c [0]은 c [1]이 True로 평가됩니다.

    두 번째 예제는 여러 개의 다른 내부 목록 객체를 만듭니다.

    >>> c = [[], [], []]  # this line creates four different lists
    >>> d = [ [] for _ in xrange(3) ]  # so does this line
    >>> c[0].append(4)
    >>> d[0].append(5)
    >>> print c
    [[4], [], []]
    >>> print d
    [[5], [], []]
    
  6. from https://stackoverflow.com/questions/17702937/generating-sublists-using-multiplication-unexpected-behavior by cc-by-sa and MIT license