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
_______________________________________________ Python-Projects mailing list Python-Projects@lists.logilab.org http://lists.logilab.org/mailman/listinfo/python-projects