b가 목록 일 때 b + = (4,)가 작동하고 b = b + (4,)가 작동하지 않는 이유는 무엇입니까?


77

우리 b = [1,2,3]가 노력하고 노력한다면 :b+=(4,)

반환 b = [1,2,3,4]하지만 우리가 시도하면 b = b + (4,)작동하지 않습니다.

b = [1,2,3]
b+=(4,) # Prints out b = [1,2,3,4]
b = b + (4,) # Gives an error saying you can't add tuples and lists

내가 예상 b+=(4,)당신이 목록과 튜플을 추가 할 수있는 실패,하지만했다. 그래서 b = b + (4,)같은 결과를 기대했지만 효과가 없었습니다.


4
나는 여기에 답을 찾을 수 있다고 믿는다 .
jochen


처음에 나는 이것을 잘못 읽고 너무 광범위하게 닫으려고 시도했다가 철회했다. 그런 다음 그것이 중복되어야한다고 생각했지만 투표를 다시 할 수 없었을뿐만 아니라 머리카락과 같은 다른 답변을 찾으려고 노력했습니다. : /
Karl Knechtel

답변:


70

"왜"질문의 문제는 대개 여러 가지 다른 것을 의미 할 수 있다는 것입니다. 나는 당신이 생각할 것으로 생각되는 각각에 대답하려고 노력할 것입니다.

"왜 다른 방식으로 작동 할 수 있습니까?" 예를 들어 this 로 대답 합니다 . 기본적으로 +=객체의 다른 메소드를 사용하려고합니다 : __iadd__(왼쪽에서만 확인 됨) vs __add____radd__(왼쪽에없는 경우 오른쪽에서 확인 된 "역 추가" __add__) 에 대한 +.

"각 버전은 정확히 무엇을합니까?" 간단히 말해서,이 list.__iadd__방법은 list.extend(언어 디자인으로 인해 여전히 과제가 있습니다)와 동일한 기능을 수행합니다.

이것은 또한 예를 들어

>>> a = [1,2,3]
>>> b = a
>>> a += [4] # uses the .extend logic, so it is still the same object
>>> b # therefore a and b are still the same list, and b has the `4` added
[1, 2, 3, 4]
>>> b = b + [5] # makes a new list and assigns back to b
>>> a # so now a is a separate list and does not have the `5`
[1, 2, 3, 4]

+물론 새로운 객체를 만들지 만 다른 시퀀스에서 요소를 가져 오는 대신 다른 목록이 명시 적으로 필요합니다.

"이것이 + =에 도움이되는 이유는 무엇입니까? 더 효율적입니다.이 extend방법은 새로운 객체를 만들 필요가 없습니다. 물론 위와 같은 놀라운 효과가 있습니다. 일반적으로 파이썬은 실제로 효율성에 관한 것이 아닙니다. 그러나 이러한 결정은 오래 전에 이루어졌습니다.

"+로리스트와 튜플을 추가하지 않는 이유는 무엇입니까?" 여기를 참조 하십시오 (감사합니다, @ splash58); 한 가지 아이디어는 (tuple + list)가 (list + tuple)과 동일한 유형을 생성해야하며 결과가 어떤 유형이어야하는지 명확하지 않습니다. 의 유형을 변경해서는 +=안되기 때문에이 문제 a += b가 없습니다 a.


2
맞아요 그리고 목록은을 사용하지 않으므로 |예제가 손상됩니다. 나는 명확 예를 생각한다면 나중에 나는 그것을 스왑 수 있습니다.
칼 Knechtel

1
|집합에 대한 Btw 는 통근 연산자이지만 +목록에 대해서는 그렇지 않습니다. 이런 이유로 나는 유형 모호성에 대한 논쟁이 특히 강하다고 생각하지 않습니다. 연산자가 출퇴근하지 않기 때문에 왜 같은 유형이 필요합니까? 결과에 lhs 유형이 있다는 것에 동의 할 수 있습니다. 반면에을 제한 list + iterator함으로써 개발자는 자신의 의도에 대해보다 명확하게 설명 할 수 있습니다. 항목으로 a확장 된 항목을 포함하는 새 목록을 작성하려는 경우 b이미이를 수행하는 방법이 new = a.copy(); new += b있습니다. 한 줄 더 있지만 맑습니다.
a_guest

a += b다른 방식으로 행동 하는 이유 a = a + b는 효율성이 아닙니다. 실제로 귀도는 선택된 행동이 혼동 되는 것으로 간주했습니다 . 리스트 a를 인자로 받아서 함수를 수행한다고 상상해보십시오 a += [1, 2, 3]. 이 구문은 새 목록을 작성하는 대신 목록을 수정하는 것처럼 보일 수 있으므로 예상 결과에 대한 대부분의 사람들의 직감에 따라 동작하도록 결정되었습니다. 그러나이 메커니즘은 ints 와 같은 불변 유형에 대해서도 작동 해야하므로 현재 설계로 이어졌습니다.
Sven Marnach '10

저는 개인적 으로 루비가했던 것처럼 단순히 속기 보다는 디자인이 혼란 스럽다고 생각 하지만, 우리가 어떻게 도착했는지 이해할 수 있습니다. a += ba = a + b
Sven Marnach '10

21

그것들은 동등하지 않습니다 :

b += (4,)

속기 :

b.extend((4,))

다음 +과 같이 목록 을 연결합니다.

b = b + (4,)

튜플을 목록에 연결하려고합니다.


14

이렇게하면 :

b += (4,)

이것으로 변환됩니다 :

b.__iadd__((4,)) 

이 호출 후드 b.extend((4,)), extend반복자를 받아이이도 작동하는 이유 :

b = [1,2,3]
b += range(2)  # prints [1, 2, 3, 0, 1]

그러나 이것을 할 때 :

b = b + (4,)

이것으로 변환됩니다 :

b = b.__add__((4,)) 

목록 객체 만 수락하십시오.


4

공식 문서에서 가변 시퀀스 유형의 경우가지 모두 :

s += t
s.extend(t)

다음과 같이 정의됩니다.

s내용으로 확장t

다음과 같이 정의 된 것과 다릅니다.

s = s + t    # not equivalent in Python!

이것은 또한 예제와 같은 튜플을 포함하여 모든 시퀀스 유형이 작동t 한다는 것을 의미 합니다 .

그러나 범위와 발전기에서도 작동합니다! 예를 들어 다음을 수행 할 수도 있습니다.

s += range(3)

3

"증강 된"할당 연산자 +=는 2000 년 10 월에 릴리스 된 Python 2.0에 도입되었습니다. 설계 및 근거는 PEP 203에 설명되어 있습니다. 이 운영자의 선언 된 목표 중 하나는 전체 작업 지원입니다. 쓰기

a = [1, 2, 3]
a += [4, 5, 6]

목록 업데이트 할 예정이다 a 장소를 . 이것은리스트에 대한 다른 참조 a, 예를 들어 a함수 인수로 수신 된 시기 가 중요합니다 .

그러나 정수 및 문자열을 포함한 많은 Python 유형은 변경할 수 없으므로 i += 1정수 i가 제대로 작동하지 않을 수 있으므로 작업이 항상 제대로 수행되지 는 않습니다.

요약하면, 확장 된 할당 연산자는 가능하면 제대로 작동하고 그렇지 않으면 새 객체를 생성해야했습니다. 이러한 설계 목표를 용이하게하기 x += y위해 다음과 같이 동작하도록 표현 을 지정했습니다.

  • 경우 x.__iadd__정의, x.__iadd__(y)평가됩니다.
  • 그렇지 않으면 x.__add__is가 구현 된 x.__add__(y)것으로 평가됩니다.
  • 그렇지 않으면 y.__radd__is가 구현 된 y.__radd__(x)것으로 평가됩니다.
  • 그렇지 않으면 오류가 발생합니다.

이 프로세스에서 얻은 첫 번째 결과 x는 ( NotImplemented단일 결과가 아닌 경우 다음 단계로 조회가 계속되는 경우) 에 다시 할당됩니다 .

이 프로세스를 통해 전체 수정을 지원하는 유형이 구현 될 수 __iadd__()있습니다. 내부 수정을 지원 하지 않는 타입은 파이썬이 자동으로 대체되기 때문에 새로운 매직 메소드를 추가 할 필요가 없습니다 x = x + y.

마지막으로 실제 할당 된 질문에 대해 살펴 보겠습니다. 왜 할당 할당 연산자를 사용하여 튜플을 목록에 추가 할 수 있습니까? 메모리에서, 이것의 역사는 대략 다음과 같았습니다 :이 list.__iadd__()메소드는 list.extend()파이썬 2.0에서 이미 존재하는 메소드 를 단순히 호출하기 위해 구현되었습니다 . 반복자가 Python 2.1에 도입되었을 때 list.extend()임의의 반복자를 허용 하도록 메소드가 업데이트되었습니다. 이러한 변경의 최종 결과는 my_list += my_tuplePython 2.1부터 작동했습니다. list.__add__()방법은, 그러나, 오른쪽 인수로 임의의 반복자를 지원하기로되지 않았다 - 이것은 강력하게 형식화 된 언어에 대한 부적절한 것으로 간주되었다.

필자는 개인적으로 증강 연산자의 구현이 파이썬에서 너무 복잡하다고 생각합니다. 다음과 같은 놀라운 부작용이 있습니다.

t = ([42], [43])
t[0] += [44]

두 번째 줄은 상승 TypeError: 'tuple' object does not support item assignment, 그러나 작업이 성공적으로 어쨌든 수행 - t될 것입니다 ([42, 44], [43])오류를 제기 라인을 실행 한 후.


브라보! PEP에 대한 참조가 특히 유용합니다. 다른 쪽 끝에 튜플 목록 동작에 대한 이전 SO 질문에 대한 링크를 추가했습니다. 2.3 이전의 파이썬의 모습을 되돌아 보면, 오늘날과 비교할 때 실제로는 사용할 수없는 것 같습니다 ... (그리고 여전히 오래된 맥에서 유용한 것을하기 위해 1.5를 얻지 못하고 있다는 막연한 기억이 있습니다)
Karl Knechtel

2

대부분의 사람들은 X + = Y가 X = X + Y와 동등 할 것으로 예상합니다. 실제로 Mark Lutz의 Python Pocket Reference (4th ed)는 57 페이지의 "다음 두 형식은 거의 동일합니다. X = X + Y, X + = Y ". 그러나 파이썬을 지정한 사람들은 그것들을 동등하게 만들지 않았습니다. 아마도 그것은 파이썬이 사용되는 한 좌절 된 프로그래머들에 의해 몇 시간의 디버깅 시간을 초래할 수있는 실수 였을 것입니다. X가 변경 가능한 시퀀스 유형 인 경우 X + = Y는 X = X + Y가 아닌 X.extend (Y)와 같습니다.


> 아마도 파이썬이 계속 사용되는 한 좌절 된 프로그래머들에 의해 몇 시간의 디버깅 시간을 초래할 수있는 실수 였을 것입니다. 당신은 경험에서 말하는 것 같습니다. 당신의 이야기를 듣고 싶습니다.
Veky

1

여기 에 설명 된 것처럼 메소드를 array구현하지 않으면 __iadd__메소드 b+=(4,)의 단축 b = b + (4,)이지만 분명히 그렇지 않습니다 . array구현 __iadd__메소드도 마찬가지 입니다. 분명히 __iadd__메소드 구현 은 다음과 같습니다.

def __iadd__(self, x):
    self.extend(x)

그러나 위의 코드는 실제 __iadd__메서드 구현이 아니라 입력 과 같은 extend메서드 가 있다고 가정하고 받아 들일 수 있습니다 tupple.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.