Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.


I'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.


The 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), you have to trash the whole line, instead of adding perhaps a single extra line of content.


> 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.


Is there any reason to use your hackery instead of the canonical form? (Other than I-am-right-syndrome)


Not sure which part you're curious about. If it wasn't clear from elsewhere in this thread...

Lambda functions are most useful for very small, straightforward functions that return a value - especially in cases where you don't necessarily need to name or document them deeply, such as for passing as arguments to other functions (for example, sort/sorted).

So if you want terse code that only does what it needs to do and nothing more, pepper with lambdas as needed.

If you want to invent names for things just cause you like inventing them or need to document every single function you write (even if the code is simple enough to document itself), feel free to make all one-line returning functions in the fully named and documented format.


Spare 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-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


> List comprehensions and generator expressions have replaced all need for map, filter, and lambdas, and are far more readable.

I would say that is debatable. Coming from an FP background, I use map, filter and lambdas nearly all of the time because I find it more readable and can easily reason about the code. I have seen some two line list comprehensions and they are far harder for me to read and understand.


OK so let's talk about that, which is more readable?:

  iterable = xrange(10)

  ge = (x*x for x in iterable if not x % 2)

  mf = map(lambda x: x*x, filter(lambda x: not x % 2, iterable))
Assume imap and ifilter from itertools or Python 3 (with range) for equivalence. I'm betting if we ask any person new to Python and new to programming, they'd think the former much more readable than the latter. Yes, we left it in the language for you cranks who think map, filter, and lambda are way better, but it's functionally no different.


> 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.


Nice 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


> Nice lambda. You've defended yourself admirably.

Thanks!

> Did you know there's an `operator.itemgetter` function that does that?

Yes, I'm quite aware! Are you aware the "useless" functional solution with lambda is two characters shorter?

  >>> len('lambda item: item[1]')
  20
  >>> len('operator.itemgetter(1)')
  22
Cause you're apparently not aware that I was demonstrating a use-case for lambdas as one-off functions that are passed to other functions (which is an abstract concept from the particular function used), and you didn't demonstrate how list comprehensions or generators make them not-needed. Of course, that's because it was a leading question and the answer is that the concepts are orthogonal so it cannot be demonstrated.


I bet you two would be good friends IRL.


I'm sure we would. I'll buy the first drink. :D


A more idiomatic way to handle this is to arrange the items in the tuple by sort precedence, then restructure the data in the tuple after the sort.

>>> [(b, a) for a, b in sorted(((-(x2), x) for x in xrange(10) if 0 == x % 2))]

[(8, -64), (6, -36), (4, -16), (2, -4), (0, 0)]

However, ocasionally you still need a more complex sorting function. So lambdas are still handy, IMO.


>> Spare us the half-baked hackery.

>Ad hominem? I guess I win.

That was not ad hominem, because what you wrote is indeed half-baked hackery.

Nobody said it cannot be done the way you did it. You were just pointed at the shortcomings of your approach and that the Python community generally prefers stupidly simple, easy to understand solutions. Using magic attributes to argue against it just makes it worse - remember this is a thread about idiomatic Python code bases.


Now you're moving goalposts...

> Nobody said it cannot be done the way you did it. You were just pointed at the shortcomings of your approach

I "addressed" the shortcomings of "my approach" by showing that Python (the language, not the community) allows you to access and manipulate the data you claimed was important and missing.

I don't believe it is necessary for most simple functions to know what their name is. I do believe the demonstrated code is self documenting enough to not require a documentation string. You made those "requirements". I never claimed that every function must be a lambda - you seem to be implying I am, so I am explicitly stating that I do not.

Here's a place where you really do need a named function (due to deficiencies in Python's lambda implementation):

  >>> def named_lambda(procedure, name, documentation=''):
  ...     procedure.__name__ = '<lambda {}>'.format(name)
  ...     procedure.__doc__ = documentation
  ...     return procedure
  ... 
  >>> absolute_path = named_lambda(lambda path: path if path.startswith('/') else '/' + path, 'absolute_path', 'Return the absolute unix path from a given path name')
  >>> absolute_path.__name__
  '<lambda absolute_path>'
  >>> absolute_path.__doc__
  'Return the absolute unix path from a given path name'
Of course, that's completely silly... since the point of a lambda function generally is that the function is generally small enough and short-lived enough that it does not need a name or documentation.

> the Python community generally prefers stupidly simple, easy to understand solutions.

Which, despite your protests, includes using lambdas!

> Using magic attributes to argue against it just makes it worse - remember this is a thread about idiomatic Python code bases.

How else does a function "know its own name" unless it uses the "magic" attribute "__name__"? Oh, you prefer "func_name"? That's cute:

  >>> named_lambda.func_name
  'named_lambda'
  >>> named_lambda.__name__
  'named_lambda'
  >>> named_lambda.__name__ = 'lol'
  >>> named_lambda.func_name
  'lol'
  >>> named_lambda.func_name = 'named_lambda'
  >>> named_lambda.func_name
  'named_lambda'
So in this comment I am replying to, it is a bad thing that I made use of "__name__", but in the comment THAT was replying to, it was a bad thing that I did NOT use "__name__" or its linked "func_name". That's how you move goal posts!


To 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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: