self.assertEqual(actual, expected)
self.assertEqual(actual_calls, expected_calls)
+ def test_assignment_idiom_in_comprehensions(self):
+ expected = {1: 1, 2: 4, 3: 9, 4: 16}
+ actual = {j: j*j for i in range(4) for j in [i+1]}
+ self.assertEqual(actual, expected)
+ expected = {3: 2, 5: 6, 7: 12, 9: 20}
+ actual = {j+k: j*k for i in range(4) for j in [i+1] for k in [j+1]}
+ self.assertEqual(actual, expected)
+ expected = {3: 2, 5: 6, 7: 12, 9: 20}
+ actual = {j+k: j*k for i in range(4) for j, k in [(i+1, i+2)]}
+ self.assertEqual(actual, expected)
+
+ def test_star_expression(self):
+ expected = {0: 0, 1: 1, 2: 4, 3: 9}
+ self.assertEqual({i: i*i for i in [*range(4)]}, expected)
+ self.assertEqual({i: i*i for i in (*range(4),)}, expected)
+
+
if __name__ == "__main__":
unittest.main()
static int compiler_sync_comprehension_generator(
struct compiler *c,
asdl_seq *generators, int gen_index,
+ int depth,
expr_ty elt, expr_ty val, int type);
static int compiler_async_comprehension_generator(
struct compiler *c,
asdl_seq *generators, int gen_index,
+ int depth,
expr_ty elt, expr_ty val, int type);
static PyCodeObject *assemble(struct compiler *, int addNone);
static int
compiler_comprehension_generator(struct compiler *c,
asdl_seq *generators, int gen_index,
+ int depth,
expr_ty elt, expr_ty val, int type)
{
comprehension_ty gen;
gen = (comprehension_ty)asdl_seq_GET(generators, gen_index);
if (gen->is_async) {
return compiler_async_comprehension_generator(
- c, generators, gen_index, elt, val, type);
+ c, generators, gen_index, depth, elt, val, type);
} else {
return compiler_sync_comprehension_generator(
- c, generators, gen_index, elt, val, type);
+ c, generators, gen_index, depth, elt, val, type);
}
}
static int
compiler_sync_comprehension_generator(struct compiler *c,
asdl_seq *generators, int gen_index,
+ int depth,
expr_ty elt, expr_ty val, int type)
{
/* generate code for the iterator, then each of the ifs,
}
else {
/* Sub-iter - calculate on the fly */
- VISIT(c, expr, gen->iter);
- ADDOP(c, GET_ITER);
+ /* Fast path for the temporary variable assignment idiom:
+ for y in [f(x)]
+ */
+ asdl_seq *elts;
+ switch (gen->iter->kind) {
+ case List_kind:
+ elts = gen->iter->v.List.elts;
+ break;
+ case Tuple_kind:
+ elts = gen->iter->v.Tuple.elts;
+ break;
+ default:
+ elts = NULL;
+ }
+ if (asdl_seq_LEN(elts) == 1) {
+ expr_ty elt = asdl_seq_GET(elts, 0);
+ if (elt->kind != Starred_kind) {
+ VISIT(c, expr, elt);
+ start = NULL;
+ }
+ }
+ if (start) {
+ VISIT(c, expr, gen->iter);
+ ADDOP(c, GET_ITER);
+ }
+ }
+ if (start) {
+ depth++;
+ compiler_use_next_block(c, start);
+ ADDOP_JREL(c, FOR_ITER, anchor);
+ NEXT_BLOCK(c);
}
- compiler_use_next_block(c, start);
- ADDOP_JREL(c, FOR_ITER, anchor);
- NEXT_BLOCK(c);
VISIT(c, expr, gen->target);
/* XXX this needs to be cleaned up...a lot! */
if (++gen_index < asdl_seq_LEN(generators))
if (!compiler_comprehension_generator(c,
- generators, gen_index,
+ generators, gen_index, depth,
elt, val, type))
return 0;
break;
case COMP_LISTCOMP:
VISIT(c, expr, elt);
- ADDOP_I(c, LIST_APPEND, gen_index + 1);
+ ADDOP_I(c, LIST_APPEND, depth + 1);
break;
case COMP_SETCOMP:
VISIT(c, expr, elt);
- ADDOP_I(c, SET_ADD, gen_index + 1);
+ ADDOP_I(c, SET_ADD, depth + 1);
break;
case COMP_DICTCOMP:
/* With '{k: v}', k is evaluated before v, so we do
the same. */
VISIT(c, expr, elt);
VISIT(c, expr, val);
- ADDOP_I(c, MAP_ADD, gen_index + 1);
+ ADDOP_I(c, MAP_ADD, depth + 1);
break;
default:
return 0;
compiler_use_next_block(c, skip);
}
compiler_use_next_block(c, if_cleanup);
- ADDOP_JABS(c, JUMP_ABSOLUTE, start);
- compiler_use_next_block(c, anchor);
+ if (start) {
+ ADDOP_JABS(c, JUMP_ABSOLUTE, start);
+ compiler_use_next_block(c, anchor);
+ }
return 1;
}
static int
compiler_async_comprehension_generator(struct compiler *c,
asdl_seq *generators, int gen_index,
+ int depth,
expr_ty elt, expr_ty val, int type)
{
comprehension_ty gen;
NEXT_BLOCK(c);
}
+ depth++;
if (++gen_index < asdl_seq_LEN(generators))
if (!compiler_comprehension_generator(c,
- generators, gen_index,
+ generators, gen_index, depth,
elt, val, type))
return 0;
break;
case COMP_LISTCOMP:
VISIT(c, expr, elt);
- ADDOP_I(c, LIST_APPEND, gen_index + 1);
+ ADDOP_I(c, LIST_APPEND, depth + 1);
break;
case COMP_SETCOMP:
VISIT(c, expr, elt);
- ADDOP_I(c, SET_ADD, gen_index + 1);
+ ADDOP_I(c, SET_ADD, depth + 1);
break;
case COMP_DICTCOMP:
/* With '{k: v}', k is evaluated before v, so we do
the same. */
VISIT(c, expr, elt);
VISIT(c, expr, val);
- ADDOP_I(c, MAP_ADD, gen_index + 1);
+ ADDOP_I(c, MAP_ADD, depth + 1);
break;
default:
return 0;
ADDOP_I(c, op, 0);
}
- if (!compiler_comprehension_generator(c, generators, 0, elt,
+ if (!compiler_comprehension_generator(c, generators, 0, 0, elt,
val, type))
goto error_in_scope;