3.と4.は、すべてのPythonバージョンで構文エラーになるはずです。ただし、Pythonバージョン2.5〜3.4に影響するバグが見つかり、その後Python課題追跡システムに投稿されました。バグのため、括弧なしのジェネレータ式は、関数に*args
and / orのみが付随している場合、関数の引数として受け入れられました**kwargs
。Python 2.6+ではケース3と4の両方が許可されていましたが、Python 2.5ではケース3のみが許可されていました。それでも、どちらも文書化された文法に反していました。
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
つまり、ドキュメントが言うの関数呼び出し備えるprimary
、ことにより、括弧内に続く(呼び出し可能に評価をすることを表現)、いずれかの引数リストまたは単にunparenthesizedジェネレータ式。引数リスト内では、すべてのジェネレータ式を括弧で囲む必要があります。
このバグは(知られていないようですが)、Python3.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がこのバグを発見したおかげで、Python3.5の新機能に記載されています。
バグの分析
Python 2.6に変更が加えられ、次の後 にキーワード引数を使用できるようになりました*args
。
関数呼び出しの* 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
停止した場所。
このバグを見つけるために、私はさまざまなPythonバージョンを試しました。2.5では次のようになります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])
そして、これはPython3.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
ただし、括弧で囲まれたジェネレーター式は、Python 3.5では機能しますが、Python3.4では機能しません。
f(*[1], (2 for x in [2]))
そしてこれが手がかりです。Python 3.5では、*splatting
が一般化されています。関数呼び出しのどこでも使用できます。
>>> print(*range(5), 42)
0 1 2 3 4 42
そのため、実際のバグ(*star
括弧なしで動作するジェネレーター)は実際にPython 3.5で修正され、Python3.4と3.5の間で変更されたバグを見つけることができました。
f((*[2, 3]), 1)
で構文エラーが発生します*
-提案についてさらに説明していただけますか?また、問題は「どうやってそれを機能させるか」ではなく、「なぜそれがこのように機能するのか」です。