Skip to content

Commit 3f7e9aa

Browse files
bpo-32925: Optimized iterating and containing test for literal lists (pythonGH-5842)
consisting of non-constants: `x in [a, b]` and `for x in [a, b]`. The case of all constant elements already was optimized.
1 parent 4e24425 commit 3f7e9aa

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

Lib/test/test_peepholer.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@
33

44
from test.bytecode_helper import BytecodeTestCase
55

6+
def count_instr_recursively(f, opname):
7+
count = 0
8+
for instr in dis.get_instructions(f):
9+
if instr.opname == opname:
10+
count += 1
11+
if hasattr(f, '__code__'):
12+
f = f.__code__
13+
for c in f.co_consts:
14+
if hasattr(c, 'co_code'):
15+
count += count_instr_recursively(c, opname)
16+
return count
17+
18+
619
class TestTranforms(BytecodeTestCase):
720

821
def test_unot(self):
@@ -311,6 +324,17 @@ def test_constant_folding(self):
311324
self.assertFalse(instr.opname.startswith('BINARY_'))
312325
self.assertFalse(instr.opname.startswith('BUILD_'))
313326

327+
def test_in_literal_list(self):
328+
def containtest():
329+
return x in [a, b]
330+
self.assertEqual(count_instr_recursively(containtest, 'BUILD_LIST'), 0)
331+
332+
def test_iterate_literal_list(self):
333+
def forloop():
334+
for x in [a, b]:
335+
pass
336+
self.assertEqual(count_instr_recursively(forloop, 'BUILD_LIST'), 0)
337+
314338

315339
class TestBuglets(unittest.TestCase):
316340

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Optimized iterating and containing test for literal lists consisting of
2+
non-constants: ``x in [a, b]`` and ``for x in [a, b]``. The case of all
3+
constant elements already was optimized.

Python/ast_opt.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,8 @@ fold_subscr(expr_ty node, PyArena *arena, int optimize)
369369
}
370370

371371
/* Change literal list or set of constants into constant
372-
tuple or frozenset respectively.
372+
tuple or frozenset respectively. Change literal list of
373+
non-constants into tuple.
373374
Used for right operand of "in" and "not in" tests and for iterable
374375
in "for" loop and comprehensions.
375376
*/
@@ -378,7 +379,21 @@ fold_iter(expr_ty arg, PyArena *arena, int optimize)
378379
{
379380
PyObject *newval;
380381
if (arg->kind == List_kind) {
381-
newval = make_const_tuple(arg->v.List.elts);
382+
/* First change a list into tuple. */
383+
asdl_seq *elts = arg->v.List.elts;
384+
Py_ssize_t n = asdl_seq_LEN(elts);
385+
for (Py_ssize_t i = 0; i < n; i++) {
386+
expr_ty e = (expr_ty)asdl_seq_GET(elts, i);
387+
if (e->kind == Starred_kind) {
388+
return 1;
389+
}
390+
}
391+
expr_context_ty ctx = arg->v.List.ctx;
392+
arg->kind = Tuple_kind;
393+
arg->v.Tuple.elts = elts;
394+
arg->v.Tuple.ctx = ctx;
395+
/* Try to create a constant tuple. */
396+
newval = make_const_tuple(elts);
382397
}
383398
else if (arg->kind == Set_kind) {
384399
newval = make_const_tuple(arg->v.Set.elts);

0 commit comments

Comments
 (0)