6 ms·
Maybe it's because I am also something of a lisper (like Norvig), but I don't see anything wrong with inline ifs (after all, that is how if works in lisp, with
by merlincorey 11y ago
Maybe it's because I am also something of a lisper (like Norvig), but I don't see anything wrong with inline ifs (after all, that is how if works in lisp, with returns for a conditional, like the ternary operator) or lambda functions. In fact, I find that improves readability dramatically because it more declaratively says what you are trying to accomplish in many cases.
For example:
absolute_path = lambda path: path if path.startswith('/') else '/' + path
To me this is perfectly clearly a simple function whose only purpose is to prepend forward slashes to unix-style paths. Is the following really so much more readable?
def absolute_path(path):
if not path.startswith('/'):
path = '/' + path
return path
To my eyes and mind, the second example is not any more readable at the expense of several lines of code.
- aaronchall 11y agoThe former is absolutely identical (semantically) to the latter, with two exceptions: 1) the former function does not know its own name and 2) the latter function can (and should) be documented with a docstring. I find the latter eminently more readable, and I work daily in a code base under development by 3000 Python developers for over 5 years. Considering that the creator of the Python language considered getting rid of lambdas because they are essentially limited functions and thus violate Python's "one obvious way to do it" philosophy, I'd rather those learning Python to be shown the latter, rather than the former. From a development and version control perspective, as soon as the lambda function requires more than a simple expression, i.e. a compound statement (https://docs.python.org/2/reference/compound_stmts.html https://docs.python.org/2/reference/compound_stmts.html), you have to trash the whole line, instead of adding perhaps a single extra line of content.
- merlincorey 11y ago> The former is absolutely identical to the latter Oh, really? Let's compare: >>> absolute_0 = lambda path: path if path.startswith('/') else '/' + path >>> def absolute_1(path): ... '''Return the absolute unix path from a given path name''' ... if not path.startswith('/'): ... path = '/' + path ... return path >>> import dis >>> dis.dis(absolute_0) 2 0 LOAD_FAST 0 (path) 3 LOAD_ATTR 0 (startswith) 6 LOAD_CONST 1 ('/') 9 CALL_FUNCTION 1 12 POP_JUMP_IF_FALSE 19 15 LOAD_FAST 0 (path) 18 RETURN_VALUE >> 19 LOAD_CONST 1 ('/') 22 LOAD_FAST 0 (path) 25 BINARY_ADD 26 RETURN_VALUE >>> dis.dis(absolute_1) 4 0 LOAD_FAST 0 (path) 3 LOAD_ATTR 0 (startswith) 6 LOAD_CONST 1 ('/') 9 CALL_FUNCTION 1 12 POP_JUMP_IF_TRUE 28 5 15 LOAD_CONST 1 ('/') 18 LOAD_FAST 0 (path) 21 BINARY_ADD 22 STORE_FAST 0 (path) 25 JUMP_FORWARD 0 (to 28) 6 >> 28 LOAD_FAST 0 (path) 31 RETURN_VALUE These functions are actually not identical in their computation - only their result. > 1) the former function does not know its own name If you think that is really important (hint: it's not [from a lisper perspective, anyway]), Python thankfully allows you to do this: >>> absolute_0.__name__ '<lambda>' >>> absolute_0.__name__ = '<lambda "absolute_0">' >>> absolute_0.__name__ '<lambda "absolute_0">' > 2) the latter function can be documented with a docstring >>> absolute_0.__doc__ >>> absolute_0.__doc__ = 'Return the absolute unix path from a given path name' >>> help(absolute_0) Help on function <lambda "absolute_0">: <lambda "absolute_0">(path) Return the absolute unix path from a given path name Of course the only reason you can't put docstrings on a lambda function in python is because the forced indentation of code and implicit return with no indented block available is what Guido went with for Lambda. > Considering that the creator of the Python language considered getting rid of lambdas Guido is not a proponent of functional programming in general and claims that map, reduce, and filter are so much harder to understand than list comprehensions (which implement some common map, reduce, and filter, operations with special optimized syntax) that he tried to get them removed from the language too. Thankfully for us users of the language, this view did not win through and we can still use map, reduce, and filter in python, if we choose.
- aaronchall 11y agoSpare us the half-baked hackery. List comprehensions and generator expressions have replaced all need for map, filter, and lambdas, and are far more readable. For someone new to Python, halfway through LPTHW, they don't need those things. Hey, maybe you can help me decipher this, I've always wondered exactly what's going on here: https://docs.python.org/2/faq/programming.html#is-it-possible-to-write-obfuscated-one-liners-in-python https://docs.python.org/2/faq/programming.html#is-it-possibl... # Mandelbrot set print (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y, Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM, Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro, i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y >=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr( 64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy ))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24) # \___ ___/ \___ ___/ | | |__ lines on screen # V V | |______ columns on screen # | | |__________ maximum of "iterations" # | |_________________ range on y axis # |____________________________ range on x axis
- merlincorey 11y ago> Spare us the half-baked hackery. Ad hominem? I guess I win. > List comprehensions and generator expressions have replaced all need for map, filter, and lambdas Please explain how list comprehensions and generators have replaced the need for lambdas. >>> sorted(((x, -(x**2)) for x in xrange(10) if 0 == x % 2), key=lambda item: item[1]) [(8, -64), (6, -36), (4, -16), (2, -4), (0, 0)] Your "distaste" of functional programming constructs is right up there with Guido's.
- aaronchall 11y agoNice lambda. You've defended yourself admirably. Did you know there's an `operator.itemgetter` function that does that? So that's: >>> sorted(((x, -(x**2)) for x in xrange(10) if 0 == x % 2), key=operator.itemgetter(1)) [(8, -64), (6, -36), (4, -16), (2, -4), (0, 0)] Do note that good code and code golf are two different things! :D
- srean 11y agoTo me the first is far more quicker to read and understand, and I am no lisper. There is something immediately gratifying about the first that I find missing in the more laborious ponderous prose of the latter.
- Xephyrous 11y agoI'd write that function like this: def absolute_path(path): if not path.startswith('/'): return '/' + path return path This way you don't have to keep track of mutating the path variable. I do find that more readable - it's clearer that there are two paths through the code. That said, this example is trivial enough that I'd probably do it with an inline if/else statement, although still probably not a lambda: def absolute_path(path): return path if path.startswith('/') else '/' + path This way it's more obvious at a glance that you're defining a function. It's easier to follow someone else's code if they generally adhere to standards, and Python is a very convention-oriented language.