IT TIP

splatting이 rhs에 튜플을 생성하지만 lhs에 목록을 생성하는 이유는 무엇입니까?

itqueen 2020. 11. 4. 21:05
반응형

splatting이 rhs에 튜플을 생성하지만 lhs에 목록을 생성하는 이유는 무엇입니까?


예를 들어,

squares = *map((2).__rpow__, range(5)),
squares
# (0, 1, 4, 9, 16)

*squares, = map((2).__rpow__, range(5))
squares
# [0, 1, 4, 9, 16]

따라서 다른 모든 것이 동일하면 lhs에 splatting 할 때 목록을 얻고 rhs에 splatting 할 때 튜플을 얻습니다.

왜?

이것은 의도 된 것입니까? 그렇다면 그 이유는 무엇입니까? 아니면 기술적 인 이유가 있습니까? 아니면 특별한 이유가 없습니까?


RHS에서 튜플을 얻는다는 사실은 표시와 관련이 없습니다. 표시는 map반복자를 압축 해제 합니다. 당신이 그것을 압축 해제 당신이 튜플 구문을 사용한 적이 있다는 사실에 의해 결정됩니다 :

*whatever,

목록 구문 대신 :

[*whatever]

또는 구문 설정 :

{*whatever}

목록이나 세트를 얻을 수 있습니다. 방금 파이썬에게 튜플을 만들도록 지시했습니다.


LHS에서 splatted 할당 대상은 항상 목록을 생성합니다. "튜플 스타일"을 사용하는지 여부는 중요하지 않습니다.

*target, = whatever

또는 "목록 스타일"

[*target] = whatever

대상 목록에 대한 구문. 구문은 목록 또는 튜플을 만드는 구문과 매우 비슷하지만 대상 목록 구문은 완전히 다릅니다.

왼쪽에서 사용하는 구문은 다음 과 같은 사용 사례를 지원하기 위해 PEP 3132 에서 도입되었습니다.

first, *rest = iterable

압축 해제 할당에서 iterable의 요소는 위치별로 별표가없는 대상에 할당되며 별표 표시된 대상이있는 경우 추가 항목이 목록에 채워지고 해당 대상에 할당됩니다. 추가 처리를 더 쉽게하기 위해 튜플 대신 목록이 선택되었습니다 . 예제에는 별표 표시된 대상 있으므로 모든 항목은 해당 대상에 할당 된 "추가"목록에 포함됩니다.


이것은 PEP-0448 단점에 명시되어 있습니다.

*elements, = iterable요소를 목록으로 elements = *iterable,만드는 동안 요소 는 튜플이됩니다. 그 이유는 구조에 익숙하지 않은 사람들을 혼동 할 수 있습니다.

또한 기준 : PEP-3132 사양

이 PEP는 반복 가능한 압축 풀기 구문에 대한 변경을 제안하여 "일반"이름에 할당되지 않은 모든 항목 목록에 할당 될 "범용"이름을 지정할 수 있습니다.

또한 여기에 언급 됨 : Python-3 전문가

목록 또는 집합 표시의 일부인 경우를 제외하고 하나 이상의 쉼표를 포함하는 표현식 목록은 튜플을 생성합니다.
후행 쉼표는 단일 튜플 (일명 단일 항목)을 만드는 데만 필요합니다. 다른 모든 경우에는 선택 사항입니다. 후행 쉼표가없는 단일 표현식은 튜플을 생성하지 않고 해당 표현식의 값을 산출합니다. (빈 튜플을 만들려면 빈 괄호 쌍을 사용하십시오 : ().)

여기에있는 더 간단한 예에서도 볼 수 있습니다.

In [27]: *elements, = range(6)                                                                                                                                                      

In [28]: elements                                                                                                                                                                   
Out[28]: [0, 1, 2, 3, 4, 5]

여기에서 요소는 튜플입니다.

In [13]: elements = *range(6),                                                                                                                                                      

In [14]: elements                                                                                                                                                                   
Out[14]: (0, 1, 2, 3, 4, 5)

댓글과 다른 답변에서 내가 이해할 수 있었던 것에서 :

  • 첫 번째 동작은 함수에서 사용되는 기존 임의 인수 목록 과 인라인을 유지하는 것입니다 .*args

  • 두 번째 동작은 평가에서 LHS의 변수를 더 아래로 사용할 수 있도록하는 것이므로 목록으로 만들면 튜플이 아닌 변경 가능한 값이 더 합리적입니다.


PEP 3132-Extended Iterable Unpacking 끝에 이유 표시가 있습니다 .

수락

python-3000 목록 [1]에 대한 짧은 토론 후 Guido는 PEP를 현재 형식으로 받아 들였습니다. 논의 된 가능한 변경 사항은 다음과 같습니다.

[...]

별표 표시된 대상을 목록 대신 튜플로 만듭니다. 이것은 함수의 * args와 일치하지만 결과의 추가 처리를 더 어렵게 만듭니다.

[1] https://mail.python.org/pipermail/python-3000/2007-May/007198.html

따라서 변경 불가능한 튜플 대신 변경 가능한 목록을 갖는 이점이 이유 인 것 같습니다.


완전한 답은 아니지만 분해하면 몇 가지 단서가 제공됩니다.

from dis import dis

def a():
    squares = (*map((2).__rpow__, range(5)),)
    # print(squares)

print(dis(a))

분해하다

  5           0 LOAD_GLOBAL              0 (map)
              2 LOAD_CONST               1 (2)
              4 LOAD_ATTR                1 (__rpow__)
              6 LOAD_GLOBAL              2 (range)
              8 LOAD_CONST               2 (5)
             10 CALL_FUNCTION            1
             12 CALL_FUNCTION            2
             14 BUILD_TUPLE_UNPACK       1
             16 STORE_FAST               0 (squares)
             18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

동안

def b():
    *squares, = map((2).__rpow__, range(5))
print(dis(b))

결과

 11           0 LOAD_GLOBAL              0 (map)
              2 LOAD_CONST               1 (2)
              4 LOAD_ATTR                1 (__rpow__)
              6 LOAD_GLOBAL              2 (range)
              8 LOAD_CONST               2 (5)
             10 CALL_FUNCTION            1
             12 CALL_FUNCTION            2
             14 UNPACK_EX                0
             16 STORE_FAST               0 (squares)
             18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

상태에 대한 문서UNPACK_EX :

UNPACK_EX (개수)

별표 표시된 대상을 사용하여 할당을 구현합니다. TOS의 이터 러블을 개별 값으로 압축 해제합니다. 여기서 총 값 수는 이터 러블의 항목 수보다 작을 수 있습니다. 새 값 중 하나는 남은 모든 항목 목록 이됩니다.

카운트의 하위 바이트는 목록 값 이전의 값 수이고 상위 바이트는 그 이후의 값 수입니다. 결과 값은 오른쪽에서 왼쪽으로 스택에 배치됩니다.

(강조 내). while BUILD_TUPLE_UNPACK반환 a tuple:

BUILD_TUPLE_UNPACK (개수)

스택에서 카운트 반복 가능 항목을 꺼내 단일 튜플 에 결합하고 결과를 푸시합니다. 튜플 디스플레이 (* x, * y, * z)에서 반복 가능한 언 패킹을 구현합니다.


RHS의 경우별로 문제가 없습니다. 여기에 대한 대답은 잘 설명합니다.

일반적으로 함수 호출에서와 같이 작동합니다. 첨부 된 iterable의 내용을 확장합니다. 따라서 진술 :

elements = *iterable

다음과 같이 볼 수 있습니다.

elements = 1, 2, 3, 4,

이것은 튜플을 초기화하는 또 다른 방법입니다.

이제 LHS의 경우, 예, LHS에 대한 기술적 인 이유가 있습니다. 이는 포장 풀기 확장을위한 초기 PEP 3132에 대한 논의에서 알 수 있습니다 .

이유는 PEP에 대한 대화에서 알 수 있습니다 (마지막에 추가됨).

기본적으로 다음과 같은 몇 가지 핵심 요소로 요약됩니다.

  • LHS는 반드시 끝으로 만 제한되지 않는 "별표 표시된 표현"을 지원해야했습니다.
  • RHS는 반복자를 포함한 다양한 시퀀스 유형을 허용해야했습니다.
  • 위의 두 점을 조합하려면 별표 표시된 표현으로 내용을 수락 한 후 내용을 조작 / 변형해야했습니다.
  • RHS에서 제공되는 반복자를 모방하고 구현의 어려움을 제쳐두고 처리에 대한 대안적인 접근 방식은 일관성이없는 동작으로 인해 Guido에 의해 중단되었습니다.
  • 위의 모든 요소를 ​​고려할 때 LHS의 튜플은 먼저 목록이 된 다음 변환되어야합니다. 이 접근 방식은 오버 헤드를 추가하고 더 이상의 논의를 유도하지 않았습니다.

요약 : 다양한 요인의 조합으로 인해 LHS 목록을 허용하기로 결정했고 그 이유는 서로 피드백되었습니다.


일치하지 않는 유형을 허용하지 않는 관련 추출 :

제안 된 의미론에 대한 Python의 중요한 사용 사례는 가변 길이 레코드가있을 때입니다. 처음 몇 개의 항목은 흥미롭고 나머지 항목은 덜 중요하지만 중요하지 않습니다. (나머지를 버리고 싶다면 a, b, c, * d = x 대신 a, b, c = x [: 3]를 작성하면됩니다.) 다음과 같은 경우이 사용 사례에 훨씬 더 편리합니다. d의 유형은 작업에 의해 고정되므로 해당 동작을 신뢰할 수 있습니다.

Python 2의 filter () 설계에 버그가 있습니다 (3.0에서 반복기 BTW로 변환하여 수정 됨) : 입력이 튜플이면 출력도 튜플이지만 입력이 목록이면 또는 그 밖의 모든 경우 출력은 목록입니다. 그것은 결과가 목록 이거나 튜플 이라는 것을 믿을 수 없다는 것을 의미하기 때문에 완전히 미친 서명입니다. 하나 또는 다른 것이 필요하다면 그것을 하나로 변환해야합니다. 시간과 공간 낭비입니다. 이 디자인 버그를 반복하지 마십시오. -귀도


또한 위의 요약과 관련하여 부분적으로 인용 된 대화를 다시 만들려고했습니다. Source Emphasis mine.

1.

인수 목록에서 * args는 반복기를 소진하여 튜플으로 변환합니다. 튜플 풀기의 * args가 똑같은 일을하지 않으면 혼란 스러울 것이라고 생각합니다.

이것은 패치가 튜플이 아닌 목록을 생성하는 이유에 대한 질문을 제기합니다. 그 이유는 무엇입니까?

STeVe

2.

IMO, 수정을 포함하여 결과 시퀀스를 추가로 처리하고 싶을 것입니다.

게오르그

삼.

글쎄, 그것이 당신이 목표로하는 것이라면, 나는 압축 풀기에서 목록이 아닌 생성하는 것이 더 유용 할 것이라고 기대 하지만, 예를 들어 문자열로 시작했다면, 나는 아마도 계속 사용하고 싶을 것입니다. strings ::- 추가 텍스트가 제거됨

4.

반복자를 다룰길이를 미리 알지 못 하므로 튜플을 얻는 유일한 방법은 먼저 목록생성 한 다음 여기에서 튜플을 만드는 것입니다. 그렉

5.

네. 그것은 * args튜플 풀기 의 끝에나타나야 한다고 제안 된 이유 중 하나입니다 .

STeVe

몇 개의 대화를 건너 뛰었습니다.

6.

주어진 유형을 반환하는 것이 시도되어야 할 목표라고 생각하지 않습니다 . 고정 된 알려진 유형 집합에 대해서만 작동 할 수 있기 때문 입니다. 임의의 시퀀스 유형이 주어지면 지정된 내용으로 새 인스턴스를 만드는 방법을 알 수 없습니다.

-그렉

건너 뛴 대화

7.

저는 다음과 같이 제안합니다.

  • 목록 반환 목록
  • 튜플은 튜플을 반환합니다.
  • XYZ 컨테이너는 XYZ 컨테이너를 반환합니다.
  • non-container iterables return iterators.

How do you propose to distinguish between the last two cases? Attempting to slice it and catching an exception is not acceptable, IMO, as it can too easily mask bugs.

-- Greg

8.

But I expect less useful. It won't support "a, *b, c = " either. From an implementation POV, if you have an unknown object on the RHS, you have to try slicing it before you try iterating over it; this may cause problems e.g. if the object happens to be a defaultdict -- since x[3:] is implemented as x[slice(None, 3, None)], the defaultdict will give you its default value. I'd much rather define this in terms of iterating over the object until it is exhausted, which can be optimized for certain known types like lists and tuples.

-- --Guido van Rossum


TLDR: You get a tuple on the RHS because you asked for one. You get a list on the LHS because it is easier.


It is important to keep in mind that the RHS is evaluated before the LHS - this is why a, b = b, a works. The difference then becomes apparent when splitting the assignment and using additional capabilities for the LHS and RHS:

# RHS: Expression List
a = head, *tail
# LHS: Target List
*leading, last = a

In short, while the two look similar, they are entirely different things. The RHS is an expression to create one tuple from all names - the LHS is a binding to multiple names from one tuple. Even if you see the LHS as a tuple of names, that does not restrict the type of each name.


The RHS is an expression list - a tuple literal without the optional () parentheses. This is the same as how 1, 2 creates a tuple even without parentheses, and how enclosing [] or {} create a list or set. The *tail just means unpacking into this tuple.

New in version 3.5: Iterable unpacking in expression lists, originally proposed by PEP 448.

The LHS does not create one value, it binds values to multiple names. With a catch-all name such as *leading, the binding is not known up-front in all cases. Instead, the catch-all contains whatever remains.

Using a list to store values makes this simples - the values for trailing names can be efficiently removed from the end. The remaining list then contains the exactly the values for the catch-all name. In fact, this is exactly what CPython does:

  • collect all items for mandatory targets before the starred one
  • collect all remaining items from the iterable in a list
  • pop items for mandatory targets after the starred one from the list
  • push the single items and the resized list on the stack

Even when the LHS has a catch-all name without trailing names, it is a list for consistency.


Using a = *b,:

If you do:

a = *[1, 2, 3],

It would give:

(1, 2, 3)

Because:

  1. Unpacking and some other stuff give tuples in default, but if you say i.e

    [*[1, 2, 3]]

    Output:

    [1, 2, 3] as a list since I do a list, so {*[1, 2, 3]} will give a set.

  2. Unpacking gives three elements, and for [1, 2, 3] it really just does

    1, 2, 3

    Which outputs:

    (1, 2, 3)

    That's what unpacking does.

The main part:

Unpacking simply executes:

1, 2, 3

For:

[1, 2, 3]

Which is a tuple:

(1, 2, 3)

Actually this creates a list, and changes it into a tuple.

Using *a, = b:

Well, this is really gonna be:

a = [1, 2, 3]

Since it isn't:

*a, b = [1, 2, 3]

Or something similar, there is not much about this.

  1. It is equivalent without * and ,, not fully, it just always gives a list.

  2. This is really almost only used for multiple variables i.e:

    *a, b = [1, 2, 3]

One thing is that no matter what it stores a list type:

>>> *a, = {1,2,3}
>>> a
[1, 2, 3]
>>> *a, = (1,2,3)
>>> a
[1, 2, 3]
>>> 

Also it would be a strange to have:

a, *b = 'hello'

And:

print(b)

To be:

'ello'

Then it doesn't seem like splatting.

Also list have more functions than others, easier to handle.

There is probably no reason for this to happen, it really a decision in Python.

The a = *b, section there is a reason, in the "The main part:" section.

Summary:

Also as @Devesh mentioned here in PEP 0448 disadvantages:

Whilst *elements, = iterable causes elements to be a list, elements = *iterable, causes elements to be a tuple. The reason for this may confuse people unfamiliar with the construct.

(emphasis mine)

Why bother, this doesn't really matter for us, why not just use the below if you want a list:

print([*a])

Or a tuple:

print((*a))

And a set:

print({*a})

And so on...

참고URL : https://stackoverflow.com/questions/56237733/why-does-splatting-create-a-tuple-on-the-rhs-but-a-list-on-the-lhs

반응형