Victor Stinner avatar Victor Stinner committed 6bf8c42

Remove invalid optimization: x in tuple => x in set

Comments (0)

Files changed (4)

    shadowed at runtime.
  - x in [a, b, c] => x in {a, b, c}.
    A list can contain non-hashable objects, a set cannot.
+   x can be non-hashable, whereas x in set() would fail.
  - [x*2 for x in "abc"] => (x*2 for x in "abc").
    The generator expression doesn't set the local variable x, and generators
    are slower than list comprehensions.
  * Replace list with tuple (need "builtin_funcs" feature). Examples:
 
    - ``for x in [1, 2, 3]: pass`` => ``for x in (1, 2, 3): pass``
+   - ``x in [1, 2, 3]`` => ``x in (1, 2, 3)``
    - ``list([x, y, z])`` => ``[x, y, z]``
    - ``set([1, 2, 3])`` => ``{1, 2, 3}`` (Python 2.7+)
 

astoptimizer/optimizer.py

         return self.compare_cst(node, op, left_cst, right_cst)
 
     def compare_in(self, data):
+        if isinstance(data, ast.List):
+            # x in [1, 2] => x in (1, 2)
+            return self.list_to_tuple(data)
+
         if sys.version_info >= (3, 2):
+            constant = self.get_constant(data, frozenset)
+            if constant is UNSET:
+                return
+            # x in frozenset((1, 2)) => x in {1, 2}
             # Python 3.2+ bytecode peepholer replaces x in {1, 2} with x in
             # frozenset({1, 2}), where frozenset({1, 2}) is a constant.
-            if isinstance(data, ast.List):
-                return new_set_elts(data, data.elts)
-
-            constant = self.get_constant(data, (tuple, frozenset))
-            if constant is UNSET:
-                return
-            # x in (1, 2) => x in {1, 2}
-            # x in frozenset((1, 2)) => x in {1, 2}
             return new_set(data, constant)
-        else:
-            if isinstance(data, ast.List):
-                # x in [1, 2] => x in (1, 2)
-                return self.list_to_tuple(data)
 
     def visit_Compare(self, node):
         # FIXME: implement 1 < 2 < 3

astoptimizer/tests.py

         self.check("not(x not in y)", self.text_ast("x in y"))
 
     def test_Compare_in(self):
+        config = self.create_config('builtin_funcs')
+
+        # constant
         self.check('"xxx" in "abcdef"', self.text_bool(False))
         self.check('"xxx" not in "abcdef"', self.text_bool(True))
         self.check('"bcd" in "abcdef"', self.text_bool(True))
         self.check('0 in (1, 2, 3)', self.text_bool(False))
         self.check('2 in (1, 2, 3)', self.text_bool(True))
 
+        # generic
+        self.check_not_optimized("x in (1, 2, 3)")
+        self.check("x in [1, 2, 3]", self.text_ast('x in (1, 2, 3)'))
+        self.check("x not in [1, 2, 3]", self.text_ast('x not in (1, 2, 3)'))
+
         if sys.version_info >= (3, 2):
-            config = self.create_config('builtin_funcs')
+            self.check("x in frozenset((1, 2, 3))", self.text_ast('x in {1, 2, 3}'), config)
+        else:
+            self.check_not_optimized("x in frozenset((1, 2, 3))", config)
 
-            self.check("x in (1, 2, 3)", self.text_ast('x in {1, 2, 3}'))
-            self.check("x in [1, 2, 3]", self.text_ast('x in {1, 2, 3}'))
-            self.check("x in frozenset((1, 2, 3))", self.text_ast('x in {1, 2, 3}'), config)
+        if sys.version_info >= (2, 7):
+            self.check("x in set((1, 2, 3))",
+                       self.text_ast("x in {1, 2, 3}"),
+                       config)
             self.check_not_optimized("x in {1, 2, 3}")
+        else:
+            self.check_not_optimized("x in set((1, 2, 3))", config)
 
-            self.check("x not in (1, 2, 3)", self.text_ast('x not in {1, 2, 3}'))
-        else:
-            self.check_not_optimized("x in (1, 2, 3)")
-            self.check("x in [1, 2, 3]", self.text_ast("x in (1, 2, 3)"))
-            self.check_not_optimized("x in set((1, 2, 3))")
-            self.check_not_optimized("x in frozenset((1, 2, 3))")
-
-            self.check("x not in [1, 2, 3]", self.text_ast('x not in (1, 2, 3)'))
         self.check("not(x in y)", self.text_ast("x not in y"))
         self.check("not(x not in y)", self.text_ast("x in y"))
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.