[Python-projects] [patch] give [deprecated-lambda] when a map/filter of a lambda could be a comprehension

Martin Pool mbp at google.com
Mon Jan 14 01:18:18 CET 2013


Hello pylint hackers,

Google's Python style guide says that rather than

  map(lambda a: a+2, numbers)   # bad

you should say

  [a+2 for a in numbers]

and similarly for filter.

I think this is pretty good style for Python generally, and Guido said this
too in <http://www.artima.com/weblogs/viewpost.jsp?thread=98196> in 2005.

So this patch will warn if you have an inline lambda as the first argument
to map or filter.  It will not complain if you construct a lambda
separately and pass it through a variable, or if you pass some other
function to map or filter.

The name is a bit generic, so if someone wants to suggest a better one that
is reasonably short please do.

--
Martin


--- pylint/checkers/base.py 2013-01-09 05:59:00.000000000 +1100
+++ pylint/checkers/base.py 2013-01-11 14:53:52.000000000 +1100
@@ -23,7 +23,12 @@
 from pylint.interfaces import IASTNGChecker
 from pylint.reporters import diff_string
 from pylint.checkers import BaseChecker, EmptyReport
-from pylint.checkers.utils import check_messages, clobber_in_except,
is_inside_except
+from pylint.checkers.utils import (
+    check_messages,
+    clobber_in_except,
+    is_inside_except,
+    safe_infer,
+    )


 import re
@@ -795,7 +800,7 @@
     msgs = {'W0107': ('Unnecessary pass statement',
                       'unnecessary-pass',
                       'Used when a "pass" statement that can be avoided is
'
-                      'encountered.)'),
+                      'encountered.'),
             }

     def visit_pass(self, node):
@@ -803,6 +808,35 @@
             self.add_message('W0107', node=node)


+class LambdaForComprehensionChecker(_BasicChecker):
+    """check for using a lambda where a comprehension would do.
+
+    See <http://www.artima.com/weblogs/viewpost.jsp?thread=98196>
+    where GvR says comprehensions would be clearer.
+    """
+
+    msgs = {'W0110': ('map/filter on lambda could be replaced by
comprehension',
+                      'deprecated-lambda',
+                      'Used when a lambda is the first argument to "map"
or '
+                      '"filter". It could be clearer as a list '
+                      'comprehension or generator expression.'),
+            }
+
+    @check_messages('W0110')
+    def visit_callfunc(self, node):
+        """visit a CallFunc node, check if map or apply are called with a
lambda
+        """
+        if not node.args:
+            return
+        if not isinstance(node.args[0], astng.Lambda):
+            return
+        infered = safe_infer(node.func)
+        if (infered
+            and infered.parent.name == '__builtin__'
+            and infered.name in ['map', 'filter']):
+            self.add_message('W0110', node=node)
+
+
 def register(linter):
     """required method to auto register this checker"""
     linter.register_checker(BasicErrorChecker(linter))
@@ -810,3 +844,4 @@
     linter.register_checker(NameChecker(linter))
     linter.register_checker(DocStringChecker(linter))
     linter.register_checker(PassChecker(linter))
+    linter.register_checker(LambdaForComprehensionChecker(linter))
--- /dev/null 2013-01-14 10:09:47.690071041 +1100
+++ pylint/test/input/func_deprecated_lambda.py 2013-01-14
10:59:09.000000000 +1100
@@ -0,0 +1,22 @@
+# pylint: disable=missing-docstring,bad-builtin,invalid-name
+__revision__ = "$Id$"
+
+# Don't do this, use a comprehension instead.
+assert map(lambda x: x*2, [1, 2, 3]) == [2, 4, 6]
+
+assert filter(lambda x: x != 1, [1, 2, 3]) == [2, 3]
+
+# It's still ok to use map and filter with anything but an inline lambda.
+double = lambda x: x * 2
+assert map(double, [1, 2, 3]) == [2, 4, 6]
+
+# It's also ok to pass lambdas to other functions.
+assert reduce(lambda x, y: x * y, [1, 2, 3, 4]) == 24
+
+# Or to a undefined function or one with varargs
+def f(*a):
+    return len(a)
+
+f(lambda x, y: x + y, [1, 2, 3])
+
+undefined_function(lambda: 2)  # pylint: disable=undefined-variable
--- /dev/null 2013-01-14 10:09:47.690071041 +1100
+++ pylint/test/messages/func_deprecated_lambda.txt 2013-01-08
14:00:01.000000000 +1100
@@ -0,0 +1,2 @@
+W:  5: map/filter on lambda could be replaced by comprehension
+W:  7: map/filter on lambda could be replaced by comprehension
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.logilab.org/pipermail/python-projects/attachments/20130114/1c0114bf/attachment.htm>


More information about the Python-Projects mailing list