programing tip

함수 인수로 생성기

itbloger 2020. 10. 6. 07:58
반응형

함수 인수로 생성기


왜 생성기를 함수에 대한 유일한 위치 인수로 전달하는 데 특별한 규칙이있는 것처럼 보이는지 설명 할 수 있습니까?

우리가 가지고있는 경우 :

>>> def f(*args):
>>>    print "Success!"
>>>    print args
  1. 예상대로 작동합니다.

    >>> f(1, *[2])
    Success!
    (1, 2)
    
  2. 예상대로 작동하지 않습니다.

    >>> f(*[2], 1)
      File "<stdin>", line 1
    SyntaxError: only named arguments may follow *expression
    
  3. 예상대로 작동합니다.

    >>> f(1 for x in [1], *[2])
    Success! 
    (generator object <genexpr> at 0x7effe06bdcd0>, 2)
    
  4. 이것은 작동하지만 이유를 이해하지 못합니다. 2)와 같은 방식으로 실패하지 않아야합니다.

    >>> f(*[2], 1 for x in [1])                                               
    Success!
    (generator object <genexpr> at 0x7effe06bdcd0>, 2)
    

3.와 4. 모두 모든 Python 버전에서 구문 오류 여야 합니다. 그러나 Python 버전 2.5-3.4에 영향을 미치는 버그를 발견했으며 이후 에 Python 문제 추적기에 게시되었습니다 . 버그로 인해 괄호로 묶지 않은 생성기 표현식이 *args및 / 또는 만 동반 된 경우 함수에 대한 인수로 허용되었습니다 **kwargs. Python 2.6+는 케이스 3과 4를 모두 허용했지만 Python 2.5는 케이스 3 만 허용했지만 둘 다 문서화 된 문법 에 위배되었습니다 .

call    ::=     primary "(" [argument_list [","]
                            | expression genexpr_for] ")"

즉, 문서를 말한다의 함수 호출 단계는 primary, 괄호로 이어 (a 호출 평가가 표현) 인수 목록 또는 단지 호로 -이지 발생기 식; 인수 목록 내에서 모든 생성기 표현식은 괄호 안에 있어야합니다.


이 버그는 (알려지지 않은 것처럼 보이지만) Python 3.5 프리 릴리즈에서 수정되었습니다. Python 3.5에서는 함수에 대한 유일한 인수가 아닌 한 항상 생성기 표현식 주위에 괄호가 필요합니다.

Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41) 
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(1 for i in [42], *a)
  File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

버그는 DeTeReR이 발견 한 덕분 에 Python 3.5의 새로운 기능에 문서화되었습니다 .


버그 분석

다음 과 같은 키워드 인수를 사용할 수 있도록 *args Python 2.6이 변경 되었습니다 .

함수 호출에 * args 인수 뒤에 키워드 인수를 제공하는 것도 합법적입니다.

>>> def f(*args, **kw):
...     print args, kw
...
>>> f(1,2,3, *(4,5,6), keyword=13)
(1, 2, 3, 4, 5, 6) {'keyword': 13}

이전에는 이것은 구문 오류였습니다. (Amaury Forgeot d' Arc 기고; 3473 호)


그러나 Python 2.6 문법 은 키워드 인수, 위치 인수 또는 베어 생성기 표현식을 구분하지 않습니다. 모두 argument파서에 대한 유형 입니다.

Python 규칙에 따라 생성기 표현식이 함수에 대한 유일한 인수가 아닌 경우 괄호로 묶어야합니다. 이것은 다음에서 확인됩니다 Python/ast.c.

for (i = 0; i < NCH(n); i++) {
    node *ch = CHILD(n, i);
    if (TYPE(ch) == argument) {
        if (NCH(ch) == 1)
            nargs++;
        else if (TYPE(CHILD(ch, 1)) == gen_for)
            ngens++;
        else
            nkeywords++;
    }
}
if (ngens > 1 || (ngens && (nargs || nkeywords))) {
    ast_error(n, "Generator expression must be parenthesized "
              "if not sole argument");
    return NULL;
}

그러나이 함수는 전혀 고려 하지 않습니다.*args 특히 일반적인 위치 인수와 키워드 인수 만 찾습니다.

동일한 함수에서 더 아래로, 키워드 arg 뒤에 비 키워드 arg에 대해 생성 된 오류 메시지가 있습니다 .

if (TYPE(ch) == argument) {
    expr_ty e;
    if (NCH(ch) == 1) {
        if (nkeywords) {
            ast_error(CHILD(ch, 0),
                      "non-keyword arg after keyword arg");
            return NULL;
        }
        ...

그러나 이것은 다음 명령문에 의해 입증 된 것처럼 괄호로 묶이지 않은 생성기 표현식 아닌 인수에 다시 적용됩니다 .else if

else if (TYPE(CHILD(ch, 1)) == gen_for) {
    e = ast_for_genexp(c, ch);
    if (!e)
        return NULL;
    asdl_seq_SET(args, nargs++, e);
}

따라서 괄호로 묶지 않은 생성기 표현식은 통과가 허용되었습니다.


이제 Python 3.5 *args에서는 함수 호출 어느 곳에서나 사용할 수 있으므로 이를 수용하기 위해 문법 이 변경되었습니다.

arglist: argument (',' argument)*  [',']

argument: ( test [comp_for] |
            test '=' test |
            '**' test |
            '*' test )

상기 for루프는 변경된

for (i = 0; i < NCH(n); i++) {
    node *ch = CHILD(n, i);
    if (TYPE(ch) == argument) {
        if (NCH(ch) == 1)
            nargs++;
        else if (TYPE(CHILD(ch, 1)) == comp_for)
            ngens++;
        else if (TYPE(CHILD(ch, 0)) == STAR)
            nargs++;
        else
            /* TYPE(CHILD(ch, 0)) == DOUBLESTAR or keyword argument */
            nkeywords++;
    }
}

따라서 버그를 수정합니다.

그러나 우연한 변화는 유효한 보이는 구조가

func(i for i in [42], *args)

func(i for i in [42], **kwargs)

괄호로 묶지 않은 생성기가 선행 *args하거나 **kwargs현재 작동을 멈춘 곳.


To locate this bug, I tried various Python versions. In 2.5 you'd get SyntaxError:

Python 2.5.5 (r255:77872, Nov 28 2010, 16:43:48) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
  File "<stdin>", line 1
    f(*[1], 2 for x in [2])

And this was fixed before some prerelease of Python 3.5:

Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41) 
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
  File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument

However, the parenthesized generator expression, it works in Python 3.5, but it does not work not in Python 3.4:

f(*[1], (2 for x in [2]))

And this is the clue. In Python 3.5 the *splatting is generalized; you can use it anywhere in a function call:

>>> print(*range(5), 42)
0 1 2 3 4 42

So the actual bug (generator working with *star without parentheses) was indeed fixed in Python 3.5, and the bug could be found in that what changed between Python 3.4 and 3.5

참고URL : https://stackoverflow.com/questions/32521140/generator-as-function-argument

반응형