I’ve heard it said that multiline lambdas can’t be added in Python because they would clash syntactically with the other syntax constructs in Python. I was thinking about this on the bus today and realized I couldn’t think of a single Python construct that multiline lambdas clash with. Given that I know the language pretty well, this surprised me.
Now, I’m sure Guido had a reason for not including multiline lambdas in the language, but out of curiosity: what’s a situation where including a multiline lambda would be ambiguous? Is what I’ve heard true, or is there some other reason that Python doesn’t allow multiline lambdas?
Is this a lambda returning (y, [1,2,3]) (thus map only gets one parameter, resulting in an error)? Or does it return y? Or is it a syntax error, because the comma on the new line is misplaced? How would Python know what you want?
Within the parens, indentation doesn’t matter to python, so you can’t unambiguously work with multilines.
This is just a simple one, there’s probably more examples.
回答 1
Guido van Rossum(Python的发明者)自己在一个旧的博客文章中回答了这个确切的问题。
基本上,他承认这在理论上是可行的,但是任何建议的解决方案都是非Python的:
Guido van Rossum (the inventor of Python) answers this exact question himself in an old blog post.
Basically, he admits that it’s theoretically possible, but that any proposed solution would be un-Pythonic:
“But the complexity of any proposed solution for this puzzle is immense, to me: it requires the parser (or more precisely, the lexer) to be able to switch back and forth between indent-sensitive and indent-insensitive modes, keeping a stack of previous modes and indentation level. Technically that can all be solved (there’s already a stack of indentation levels that could be generalized). But none of that takes away my gut feeling that it is all an elaborate Rube Goldberg contraption.”
This is generally very ugly (but sometimes the alternatives are even more ugly), so a workaround is to make a braces expression:
lambda: (
doFoo('abc'),
doBar(123),
doBaz())
It won’t accept any assignments though, so you’ll have to prepare data beforehand.
The place I found this useful is the PySide wrapper, where you sometimes have short callbacks. Writing additional member functions would be even more ugly. Normally you won’t need this.
For a while, I was following the development of Reia, which was initially going to have Python’s indentation based syntax with Ruby blocks too, all on top of Erlang. But, the designer wound up giving up on indentation sensitivity, and this post he wrote about that decision includes a discussion about problems he ran into with indentation + multi-line blocks, and an increased appreciation he gained for Guido’s design issues/decisions:
Also, here’s an interesting proposal for Ruby-style blocks in Python I ran across where Guido posts a response w/o actually shooting it down (not sure whether there has been any subsequent shoot down, though):
[Edit] Read this answer. It explains why multiline lambda is not a thing.
Simply put, it’s unpythonic. From Guido van Rossum’s blog post:
I find any solution unacceptable that embeds an indentation-based block in the middle of an expression. Since I find alternative syntax for statement grouping (e.g. braces or begin/end keywords) equally unacceptable, this pretty much makes a multi-line lambda an unsolvable puzzle.
回答 5
让我向您介绍一个光荣却可怕的技巧:
import types
def _obj():returnlambda:Nonedef LET(bindings, body, env=None):'''Introduce local bindings.
ex: LET(('a', 1,
'b', 2),
lambda o: [o.a, o.b])
gives: [1, 2]
Bindings down the chain can depend on
the ones above them through a lambda.
ex: LET(('a', 1,
'b', lambda o: o.a + 1),
lambda o: o.b)
gives: 2
'''if len(bindings)==0:return body(env)
env = env or _obj()
k, v = bindings[:2]if isinstance(v, types.FunctionType):
v = v(env)
setattr(env, k, v)return LET(bindings[2:], body, env)
您现在可以按以下LET方式使用此表单:
map(lambda x: LET(('y', x +1,'z', x -1),lambda o: o.y * o.z),[1,2,3])
Let me try to tackle @balpha parsing problem. I would use parentheses around the multiline lamda. If there is no parentheses, the lambda definition is greedy. So the lambda in
map(lambda x:
y = x+1
z = x-1
y*z,
[1,2,3]))
returns a function that returns (y*z, [1,2,3])
But
map((lambda x:
y = x+1
z = x-1
y*z)
,[1,2,3]))
means
map(func, [1,2,3])
where func is the multiline lambda that return y*z. Does that work?
Consider this (includes even usage of statements’ return values in further statements within the “multiline” lambda, although it’s ugly to the point of vomiting ;-)
Maybe it does not answer exactly the question if that was how to do multiline in the lambda expression itself, but in case somebody gets this thread looking how to debug the expression (like me) I think it will help
回答 11
关于丑陋的黑客,您始终可以使用exec和常规函数的组合来定义多行函数,如下所示:
f =exec('''
def mlambda(x, y):
d = y - x
return d * d
''', globals())or mlambda
您可以将其包装为以下函数:
def mlambda(signature,*lines):
exec_vars ={}exec('def mlambda'+ signature +':\n'+'\n'.join('\t'+ line for line in lines), exec_vars)return exec_vars['mlambda']
f = mlambda('(x, y)','d = y - x','return d * d')
On the subject of ugly hacks, you can always use a combination of exec and a regular function to define a multiline function like this:
f = exec('''
def mlambda(x, y):
d = y - x
return d * d
''', globals()) or mlambda
You can wrap this into a function like:
def mlambda(signature, *lines):
exec_vars = {}
exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
return exec_vars['mlambda']
f = mlambda('(x, y)',
'd = y - x',
'return d * d')
def let(*funcs):def wrap(args):
result = args
for func in funcs:ifnot isinstance(result, tuple):
result =(result,)
result = func(*result)return result
return wrap
Here’s a more interesting implementation of multi line lambdas. It’s not possible to achieve because of how python use indents as a way to structure code.
But luckily for us, indent formatting can be disabled using arrays and parenthesis.
As some already pointed out, you can write your code as such:
lambda args: (expr1, expr2,... exprN)
In theory if you’re guaranteed to have evaluation from left to right it would work but you still lose values being passed from one expression to an other.
One way to achieve that which is a bit more verbose is to have
lambda args: [lambda1, lambda2, ..., lambdaN]
Where each lambda receives arguments from the previous one.
def let(*funcs):
def wrap(args):
result = args
for func in funcs:
if not isinstance(result, tuple):
result = (result,)
result = func(*result)
return result
return wrap
This method let you write something that is a bit lisp/scheme like.
So you can write things like this:
let(lambda x, y: x+y)((1, 2))
A more complex method could be use to compute the hypotenuse
This will return a list of scalar numbers so it can be used to reduce multiple values to one.
Having that many lambda is certainly not going to be very efficient but if you’re constrained it can be a good way to get something done quickly then rewrite it as an actual function later.
回答 14
因为lambda函数应该是单行的,因此它是函数的最简单形式, an entrance, then return
A statistics module has been added to python 3.4. It has a function to calculate the average called mean. An example with the list you provided would be:
from statistics import mean
l = [15, 18, 2, 36, 12, 78, 5, 6, 9]
mean(l)
In order to use reduce for taking a running average, you’ll need to track the total but also the total number of elements seen so far. since that’s not a trivial element in the list, you’ll also have to pass reduce an extra argument to fold into.
Both can give you close to similar values on an integer or at least 10 decimal values. But if you are really considering long floating values both can be different. Approach can vary on what you want to achieve.
>>> l = [15, 18, 2, 36, 12, 78, 5, 6, 9]
>>> print reduce(lambda x, y: x + y, l) / len(l)
20
>>> sum(l)/len(l)
20
Floating values
>>> print reduce(lambda x, y: x + y, l) / float(len(l))
20.1111111111
>>> print sum(l)/float(len(l))
20.1111111111
@Andrew Clark was correct on his statement.
回答 15
假设
x = [[-5.01,-5.43,1.08,0.86,-2.67,4.94,-2.51,-2.25,5.56,1.03],
[-8.12,-3.48,-5.52,-3.78,0.63,3.29,2.09,-2.13,2.86,-3.33],
[-3.68,-3.54,1.66,-4.11,7.39,2.08,-2.59,-6.94,-2.26,4.33]]
x = [[-5.01,-5.43,1.08,0.86,-2.67,4.94,-2.51,-2.25,5.56,1.03],
[-8.12,-3.48,-5.52,-3.78,0.63,3.29,2.09,-2.13,2.86,-3.33],
[-3.68,-3.54,1.66,-4.11,7.39,2.08,-2.59,-6.94,-2.26,4.33]]
you can notice that x has dimension 3*10 if you need to get the mean to each row you can type this
theMean = np.mean(x1,axis=1)
don’t forget to import numpy as np
回答 16
l =[15,18,2,36,12,78,5,6,9]
l = map(float,l)print'%.2f'%(sum(l)/len(l))
from operator import truediv
L =[15,18,2,36,12,78,5,6,9]def sum_and_count(x, y):try:return(x[0]+ y, x[1]+1)exceptTypeError:return(x + y,2)
truediv(*reduce(sum_and_count, L))# prints 20.11111111111111
Combining a couple of the above answers, I’ve come up with the following which works with reduce and doesn’t assume you have L available inside the reducing function:
from operator import truediv
L = [15, 18, 2, 36, 12, 78, 5, 6, 9]
def sum_and_count(x, y):
try:
return (x[0] + y, x[1] + 1)
except TypeError:
return (x + y, 2)
truediv(*reduce(sum_and_count, L))
# prints
20.11111111111111
numbers =[0,1,2,3]
numbers[0]= input("Please enter a number")
numbers[1]= input("Please enter a second number")
numbers[2]= input("Please enter a third number")
numbers[3]= input("Please enter a fourth number")print(numbers)print("Finding the Avarage")
avarage = int(numbers[0])+ int(numbers[1])+ int(numbers[2])+ int(numbers [3])/4print(avarage)
numbers = [0,1,2,3]
numbers[0] = input("Please enter a number")
numbers[1] = input("Please enter a second number")
numbers[2] = input("Please enter a third number")
numbers[3] = input("Please enter a fourth number")
print (numbers)
print ("Finding the Avarage")
avarage = int(numbers[0]) + int(numbers[1]) + int(numbers[2]) + int(numbers [3]) / 4
print (avarage)
I’m trying to figure out Python lambdas. Is lambda one of those “interesting” language items that in real life should be forgotten?
I’m sure there are some edge cases where it might be needed, but given the obscurity of it, the potential of it being redefined in future releases (my assumption based on the various definitions of it) and the reduced coding clarity – should it be avoided?
This reminds me of overflowing (buffer overflow) of C types – pointing to the top variable and overloading to set the other field values. It feels like sort of a techie showmanship but maintenance coder nightmare.
Those things are actually quite useful. Python supports a style of programming called functional programming where you can pass functions to other functions to do stuff. Example:
Of course, in this particular case, you could do the same thing as a list comprehension:
mult3 = [x for x in [1, 2, 3, 4, 5, 6, 7, 8, 9] if x % 3 == 0]
(or even as range(3,10,3)), but there are many other, more sophisticated use cases where you can’t use a list comprehension and a lambda function may be the shortest way to write something out.
Returning a function from another function
>>> def transform(n):
... return lambda x: x + n
...
>>> f = transform(3)
>>> f(4)
7
This is often used to create function wrappers, such as Python’s decorators.
Combining elements of an iterable sequence with reduce()
I use lambda functions on a regular basis. It took me a while to get used to them, but eventually I came to understand that they’re a very valuable part of the language.
lambda is just a fancy way of saying function. Other than its name, there is nothing obscure, intimidating or cryptic about it. When you read the following line, replace lambda by function in your mind:
>>> f = lambda x: x + 1
>>> f(3)
4
It just defines a function of x. Some other languages, like R, say it explicitly:
> f = function(x) { x + 1 }
> f(3)
4
You see? It’s one of the most natural things to do in programming.
Closures: Very useful. Learn them, use them, love them.
Python’s lambda keyword: unnecessary, occasionally useful. If you find yourself doing anything remotely complex with it, put it away and define a real function.
A lambda is part of a very important abstraction mechanism which deals with higher order functions. To get proper understanding of its value, please watch high quality lessons from Abelson and Sussman, and read the book SICP
These are relevant issues in modern software business, and becoming ever more popular.
lambdas are extremely useful in GUI programming. For example, lets say you’re creating a group of buttons and you want to use a single paramaterized callback rather than a unique callback per button. Lambda lets you accomplish that with ease:
for value in ["one","two","three"]:
b = tk.Button(label=value, command=lambda arg=value: my_callback(arg))
b.pack()
(Note: although this question is specifically asking about lambda, you can also use functools.partial to get the same type of result)
The alternative is to create a separate callback for each button which can lead to duplicated code.
Curiously, the map, filter, and reduce functions that originally motivated the introduction of lambda and other functional features have to a large extent been superseded by list comprehensions and generator expressions. In fact, the reduce function was removed from list of builtin functions in Python 3.0. (However, it’s not necessary to send in complaints about the removal of lambda, map or filter: they are staying. :-)
My own two cents: Rarely is lambda worth it as far as clarity goes. Generally there is a more clear solution that doesn’t include lambda.
In Python, lambda is just a way of defining functions inline,
a = lambda x: x + 1
print a(1)
and..
def a(x): return x + 1
print a(1)
..are the exact same.
There is nothing you can do with lambda which you cannot do with a regular function—in Python functions are an object just like anything else, and lambdas simply define a function:
>>> a = lambda x: x + 1
>>> type(a)
<type 'function'>
I honestly think the lambda keyword is redundant in Python—I have never had the need to use them (or seen one used where a regular function, a list-comprehension or one of the many builtin functions could have been better used instead)
To see how lambda is broken, try generating a list of functions fs=[f0,...,f9] where fi(n)=i+n. First attempt:
>>> fs = [(lambda n: i + n) for i in range(10)]
>>> fs[3](4)
13
I would argue, even if that did work, it’s horribly and “unpythonic”, the same functionality could be written in countless other ways, for example:
>>> n = 4
>>> [i + n for i in range(10)]
[4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Yes, it’s not the same, but I have never seen a cause where generating a group of lambda functions in a list has been required. It might make sense in other languages, but Python is not Haskell (or Lisp, or …)
Please note that we can use lambda and still achieve the desired
results in this way :
>>> fs = [(lambda n,i=i: i + n) for i in range(10)]
>>> fs[3](4)
7
Edit:
There are a few cases where lambda is useful, for example it’s often convenient when connecting up signals in PyQt applications, like this:
w = PyQt4.QtGui.QLineEdit()
w.textChanged.connect(lambda event: dothing())
Just doing w.textChanged.connect(dothing) would call the dothing method with an extra event argument and cause an error. Using the lambda means we can tidily drop the argument without having to define a wrapping function.
plural_rules =[lambda n:'all',lambda n:'singular'if n ==1else'plural',lambda n:'singular'if0<= n <=1else'plural',...]# Call plural rule #1 with argument 4 to find out which sentence form to use.
plural_rule[1](4)# returns 'plural'
plural_rules = [
lambda n: 'all',
lambda n: 'singular' if n == 1 else 'plural',
lambda n: 'singular' if 0 <= n <= 1 else 'plural',
...
]
# Call plural rule #1 with argument 4 to find out which sentence form to use.
plural_rule[1](4) # returns 'plural'
If you’d have to define a function for all of those you’d go mad by the end of it.
Also, it wouldn’t be nice with function names like plural_rule_1, plural_rule_2, etc. And you’d need to eval() it when you’re depending on a variable function id.
Pretty much anything you can do with lambda you can do better with either named functions or list and generator expressions.
Consequently, for the most part you should just one of those in basically any situation (except maybe for scratch code written in the interactive interpreter).
I’ve been using Python for a few years and I’ve never run in to a case where I’ve needed lambda. Really, as the tutorial states, it’s just for syntactic sugar.
I can’t speak to python’s particular implementation of lambda, but in general lambda functions are really handy. They’re a core technique (maybe even THE technique) of functional programming, and they’re also very useuful in object-oriented programs. For certain types of problems, they’re the best solution, so certainly shouldn’t be forgotten!
I suggest you read up on closures and the map function (that links to python docs, but it exists in nearly every language that supports functional constructs) to see why it’s useful.
回答 11
Lambda函数是创建函数的非官僚方式。
而已。例如,让我们假设您具有主要功能并且需要对值进行平方。让我们看看传统的方法和lambda方法:
传统方式:
def main():......
y = square(some_number)...return something
def square(x):return x**2
Lambda方法:
def main():...
square =lambda x: x**2
y = square(some_number)return something
Lambda function it’s a non-bureaucratic way to create a function.
That’s it. For example, let’s supose you have your main function and need to square values. Let’s see the traditional way and the lambda way to do this:
Lambda functions go very well with lists, like lists comprehensions or map. In fact, list comprehension it’s a “pythonic” way to express yourself using lambda. Ex:
>>>a = [1,2,3,4]
>>>[x**2 for x in a]
[1,4,9,16]
Let’s see what each elements of the syntax means:
[] : “Give me a list”
x**2 : “using this new-born function”
for x in a: “into each element in a”
That’s convenient uh? Creating functions like this. Let’s rewrite it using lambda:
>>> square = lambda x: x**2
>>> [square(s) for x in a]
[1,4,9,16]
Now let’s use map, which is the same thing, but more language-neutral. Maps takes 2 arguments:
(i) one function
(ii) an iterable
And gives you a list where each element it’s the function applied to each element of the iterable.
So, using map we would have:
>>> a = [1,2,3,4]
>>> squared_list = map(lambda x: x**2, a)
If you master lambdas and mapping, you will have a great power to manipulate data and in a concise way. Lambda functions are neither obscure nor take away code clarity. Don’t confuse something hard with something new. Once you start using them, you will find it very clear.
One of the nice things about lambda that’s in my opinion understated is that it’s way of deferring an evaluation for simple forms till the value is needed. Let me explain.
Many library routines are implemented so that they allow certain parameters to be callables (of whom lambda is one). The idea is that the actual value will be computed only at the time when it’s going to be used (rather that when it’s called). An (contrived) example might help to illustrate the point. Suppose you have a routine which which was going to do log a given timestamp. You want the routine to use the current time minus 30 minutes. You’d call it like so
Now suppose the actual function is going to be called only when a certain event occurs and you want the timestamp to be computed only at that time. You can do this like so
As stated above, the lambda operator in Python defines an anonymous function, and in Python functions are closures. It is important not to confuse the concept of closures with the operator lambda, which is merely syntactic methadone for them.
When I started in Python a few years ago, I used lambdas a lot, thinking they were cool, along with list comprehensions. However, I wrote and have to maintain a big website written in Python, with on the order of several thousand function points. I’ve learnt from experience that lambdas might be OK to prototype things with, but offer nothing over inline functions (named closures) except for saving a few key-stokes, or sometimes not.
Basically this boils down to several points:
it is easier to read software that is explicitly written using meaningful names. Anonymous closures by definition cannot have a meaningful name, as they have no name. This brevity seems, for some reason, to also infect lambda parameters, hence we often see examples like lambda x: x+1
it is easier to reuse named closures, as they can be referred to by name more than once, when there is a name to refer to them by.
it is easier to debug code that is using named closures instead of lambdas, because the name will appear in tracebacks, and around the error.
That’s enough reason to round them up and convert them to named closures. However, I hold two other grudges against anonymous closures.
The first grudge is simply that they are just another unnecessary keyword cluttering up the language.
The second grudge is deeper and on the paradigm level, i.e. I do not like that they promote a functional-programming style, because that style is less flexible than the message passing, object oriented or procedural styles, because the lambda calculus is not Turing-complete (luckily in Python, we can still break out of that restriction even inside a lambda). The reasons I feel lambdas promote this style are:
There is an implicit return, i.e. they seem like they ‘should’ be functions.
They are an alternative state-hiding mechanism to another, more explicit, more readable, more reusable and more general mechanism: methods.
I try hard to write lambda-free Python, and remove lambdas on sight. I think Python would be a slightly better language without lambdas, but that’s just my opinion.
def main():# define widgets and other imp stuff
x, y =None,None
widget.bind("<Button-1>",lambda event:do-something-cool(x, y))defdo-something-cool(event, x, y):
x = event.x
y = event.y
#Do other cool stuff
Lambdas are actually very powerful constructs that stem from ideas in functional programming, and it is something that by no means will be easily revised, redefined or removed in the near future of Python. They help you write code that is more powerful as it allows you to pass functions as parameters, thus the idea of functions as first-class citizens.
Lambdas do tend to get confusing, but once a solid understanding is obtained, you can write clean elegant code like this:
squared = map(lambda x: x*x, [1, 2, 3, 4, 5])
The above line of code returns a list of the squares of the numbers in the list. Ofcourse, you could also do it like:
It is obvious the former code is shorter, and this is especially true if you intend to use the map function (or any similar function that takes a function as a parameter) in only one place. This also makes the code more intuitive and elegant.
Also, as @David Zaslavsky mentioned in his answer, list comprehensions are not always the way to go especially if your list has to get values from some obscure mathematical way.
From a more practical standpoint, one of the biggest advantages of lambdas for me recently has been in GUI and event-driven programming. If you take a look at callbacks in Tkinter, all they take as arguments are the event that triggered them. E.g.
def define_bindings(widget):
widget.bind("<Button-1>", do-something-cool)
def do-something-cool(event):
#Your code to execute on the event trigger
Now what if you had some arguments to pass? Something as simple as passing 2 arguments to store the coordinates of a mouse-click. You can easily do it like this:
def main():
# define widgets and other imp stuff
x, y = None, None
widget.bind("<Button-1>", lambda event: do-something-cool(x, y))
def do-something-cool(event, x, y):
x = event.x
y = event.y
#Do other cool stuff
Now you can argue that this can be done using global variables, but do you really want to bang your head worrying about memory management and leakage especially if the global variable will just be used in one particular place? That would be just poor programming style.
In short, lambdas are awesome and should never be underestimated. Python lambdas are not the same as LISP lambdas though (which are more powerful), but you can really do a lot of magical stuff with them.
Lambdas are deeply linked to functional programming style in general. The idea that you can solve problems by applying a function to some data, and merging the results, is what google uses to implement most of its algorithms.
Programs written in functional programming style, are easily parallelized and hence are becoming more and more important with modern multi-core machines.
So in short, NO you should not forget them.
First congrats that managed to figure out lambda. In my opinion this is really powerful construct to act with. The trend these days towards functional programming languages is surely an indicator that it neither should be avoided nor it will be redefined in the near future.
You just have to think a little bit different. I’m sure soon you will love it. But be careful if you deal only with python. Because the lambda is not a real closure, it is “broken” somehow: pythons lambda is broken
I’m just beginning Python and ran head first into Lambda- which took me a while to figure out.
Note that this isn’t a condemnation of anything. Everybody has a different set of things that don’t come easily.
Is lambda one of those ‘interesting’ language items that in real life should be forgotten?
No.
I’m sure there are some edge cases where it might be needed, but given the obscurity of it,
It’s not obscure. The past 2 teams I’ve worked on, everybody used this feature all the time.
the potential of it being redefined in future releases (my assumption based on the various definitions of it)
I’ve seen no serious proposals to redefine it in Python, beyond fixing the closure semantics a few years ago.
and the reduced coding clarity – should it be avoided?
It’s not less clear, if you’re using it right. On the contrary, having more language constructs available increases clarity.
This reminds me of overflowing (buffer overflow) of C types – pointing to the top variable and overloading to set the other field values…sort of a techie showmanship but maintenance coder nightmare..
Lambda is like buffer overflow? Wow. I can’t imagine how you’re using lambda if you think it’s a “maintenance nightmare”.
I started reading David Mertz’s book today ‘Text Processing in Python.’ While he has a fairly terse description of Lambda’s the examples in the first chapter combined with the explanation in Appendix A made them jump off the page for me (finally) and all of a sudden I understood their value. That is not to say his explanation will work for you and I am still at the discovery stage so I will not attempt to add to these responses other than the following:
I am new to Python
I am new to OOP
Lambdas were a struggle for me
Now that I read Mertz, I think I get them and I see them as very useful as I think they allow a cleaner approach to programming.
He reproduces the Zen of Python, one line of which is Simple is better than complex. As a non-OOP programmer reading code with lambdas (and until last week list comprehensions) I have thought-This is simple?. I finally realized today that actually these features make the code much more readable, and understandable than the alternative-which is invariably a loop of some sort. I also realized that like financial statements-Python was not designed for the novice user, rather it is designed for the user that wants to get educated. I can’t believe how powerful this language is. When it dawned on me (finally) the purpose and value of lambdas I wanted to rip up about 30 programs and start over putting in lambdas where appropriate.
A useful case for using lambdas is to improve the readability of long list comprehensions.
In this example loop_dic is short for clarity but imagine loop_dic being very long. If you would just use a plain value that includes i instead of the lambda version of that value you would get a NameError.
I can give you an example where I actually needed lambda serious. I’m making a graphical program, where the use right clicks on a file and assigns it one of three options. It turns out that in Tkinter (the GUI interfacing program I’m writing this in), when someone presses a button, it can’t be assigned to a command that takes in arguments. So if I chose one of the options and wanted the result of my choice to be:
print 'hi there'
Then no big deal. But what if I need my choice to have a particular detail. For example, if I choose choice A, it calls a function that takes in some argument that is dependent on the choice A, B or C, TKinter could not support this. Lamda was the only option to get around this actually…
I use lambda to create callbacks that include parameters. It’s cleaner writing a lambda in one line than to write a method to perform the same functionality.
Lambda is a procedure constructor. You can synthesize programs at run-time, although Python’s lambda is not very powerful. Note that few people understand that kind of programming.
It’s more readable, and if needed for performance the lambda could be taken out to gain something.
Question is: are there any caveats in using the second way? Any performance difference? Am I missing the Pythonic Way™ entirely and should do it in yet another way (such as using itemgetter instead of the lambda)?
It is strange how much beauty varies for different people. I find the list comprehension much clearer than filter+lambda, but use whichever you find easier.
There are two things that may slow down your use of filter.
The first is the function call overhead: as soon as you use a Python function (whether created by def or lambda) it is likely that filter will be slower than the list comprehension. It almost certainly is not enough to matter, and you shouldn’t think much about performance until you’ve timed your code and found it to be a bottleneck, but the difference will be there.
The other overhead that might apply is that the lambda is being forced to access a scoped variable (value). That is slower than accessing a local variable and in Python 2.x the list comprehension only accesses local variables. If you are using Python 3.x the list comprehension runs in a separate function so it will also be accessing value through a closure and this difference won’t apply.
The other option to consider is to use a generator instead of a list comprehension:
def filterbyvalue(seq, value):
for el in seq:
if el.attribute==value: yield el
Then in your main code (which is where readability really matters) you’ve replaced both list comprehension and filter with a hopefully meaningful function name.
Personally I find list comprehensions easier to read. It is more explicit what is happening from the expression [i for i in list if i.attribute == value] as all the behaviour is on the surface not inside the filter function.
I would not worry too much about the performance difference between the two approaches as it is marginal. I would really only optimise this if it proved to be the bottleneck in your application which is unlikely.
Also since the BDFL wanted filter gone from the language then surely that automatically makes list comprehensions more Pythonic ;-)
Since any speed difference is bound to be miniscule, whether to use filters or list comprehensions comes down to a matter of taste. In general I’m inclined to use comprehensions (which seems to agree with most other answers here), but there is one case where I prefer filter.
A very frequent use case is pulling out the values of some iterable X subject to a predicate P(x):
[x for x in X if P(x)]
but sometimes you want to apply some function to the values first:
[f(x) for x in X if P(f(x))]
As a specific example, consider
primes_cubed = [x*x*x for x in range(1000) if prime(x)]
I think this looks slightly better than using filter. But now consider
prime_cubes = [x*x*x for x in range(1000) if prime(x*x*x)]
In this case we want to filter against the post-computed value. Besides the issue of computing the cube twice (imagine a more expensive calculation), there is the issue of writing the expression twice, violating the DRY aesthetic. In this case I’d be apt to use
prime_cubes = filter(prime, [x*x*x for x in range(1000)])
Although filter may be the “faster way”, the “Pythonic way” would be not to care about such things unless performance is absolutely critical (in which case you wouldn’t be using Python!).
I thought I’d just add that in python 3, filter() is actually an iterator object, so you’d have to pass your filter method call to list() in order to build the filtered list. So in python 2:
lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = filter(lambda num: num % 2 == 0, lst_a)
lists b and c have the same values, and were completed in about the same time as filter() was equivalent [x for x in y if z]. However, in 3, this same code would leave list c containing a filter object, not a filtered list. To produce the same values in 3:
lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = list(filter(lambda num: num %2 == 0, lst_a))
The problem is that list() takes an iterable as it’s argument, and creates a new list from that argument. The result is that using filter in this way in python 3 takes up to twice as long as the [x for x in y if z] method because you have to iterate over the output from filter() as well as the original list.
An important difference is that list comprehension will return a list while the filter returns a filter, which you cannot manipulate like a list (ie: call len on it, which does not work with the return of filter).
My own self-learning brought me to some similar issue.
That being said, if there is a way to have the resulting list from a filter, a bit like you would do in .NET when you do lst.Where(i => i.something()).ToList(), I am curious to know it.
EDIT: This is the case for Python 3, not 2 (see discussion in comments).
Filter is just that. It filters out the elements of a list. You can see the definition mentions the same(in the official docs link I mentioned before). Whereas, list comprehension is something that produces a new list after acting upon something on the previous list.(Both filter and list comprehension creates new list and not perform operation in place of the older list. A new list here is something like a list with, say, an entirely new data type. Like converting integers to string ,etc)
In your example, it is better to use filter than list comprehension, as per the definition. However, if you want, say other_attribute from the list elements, in your example is to be retrieved as a new list, then you can use list comprehension.
return [item.other_attribute for item in my_list if item.attribute==value]
This is how I actually remember about filter and list comprehension. Remove a few things within a list and keep the other elements intact, use filter. Use some logic on your own at the elements and create a watered down list suitable for some purpose, use list comprehension.
# Throw out blank lines and commentswith open('file.txt','r')as lines:# From the inside out:# [s.partition('#')[0].strip() for s in lines]... Throws out comments# filter(lambda x: x!= '', [s.part... Filters out blank lines# y for y in filter... Converts filter object to list
file_contents =[y for y in filter(lambda x: x !='',[s.partition('#')[0].strip()for s in lines])]
Here’s a short piece I use when I need to filter on something after the list comprehension. Just a combination of filter, lambda, and lists (otherwise known as the loyalty of a cat and the cleanliness of a dog).
In this case I’m reading a file, stripping out blank lines, commented out lines, and anything after a comment on a line:
# Throw out blank lines and comments
with open('file.txt', 'r') as lines:
# From the inside out:
# [s.partition('#')[0].strip() for s in lines]... Throws out comments
# filter(lambda x: x!= '', [s.part... Filters out blank lines
# y for y in filter... Converts filter object to list
file_contents = [y for y in filter(lambda x: x != '', [s.partition('#')[0].strip() for s in lines])]
In addition to the accepted answer, there is a corner case when you should use filter instead of a list comprehension. If the list is unhashable you cannot directly process it with a list comprehension. A real world example is if you use pyodbc to read results from a database. The fetchAll() results from cursor is an unhashable list. In this situation, to directly manipulating on the returned results, filter should be used:
cursor.execute("SELECT * FROM TABLE1;")
data_from_db = cursor.fetchall()
processed_data = filter(lambda s: 'abc' in s.field1 or s.StartTime >= start_date_time, data_from_db)
If you use list comprehension here you will get the error:
TypeError: unhashable type: ‘list’
回答 11
我花了一些时间熟悉higher order functionsfilterand map。因此,我习惯了它们,实际上我很喜欢filter,因为很明显它通过保留真实内容来进行过滤,而且我知道一些functional programming术语也很酷。
It took me some time to get familiarized with the higher order functionsfilter and map. So i got used to them and i actually liked filter as it was explicit that it filters by keeping whatever is truthy and I’ve felt cool that I knew some functional programming terms.
Then I read this passage (Fluent Python Book):
The map and filter functions are still builtins
in Python 3, but since the introduction of list comprehensions and generator ex‐
pressions, they are not as important. A listcomp or a genexp does the job of map and
filter combined, but is more readable.
And now I think, why bother with the concept of filter / map if you can achieve it with already widely spread idioms like list comprehensions. Furthermore maps and filters are kind of functions. In this case I prefer using Anonymous functions lambdas.
Finally, just for the sake of having it tested, I’ve timed both methods (map and listComp) and I didn’t see any relevant speed difference that would justify making arguments about it.
from timeit import Timer
timeMap = Timer(lambda: list(map(lambda x: x*x, range(10**7))))
print(timeMap.timeit(number=100))
timeListComp = Timer(lambda:[(lambda x: x*x) for x in range(10**7)])
print(timeListComp.timeit(number=100))
#Map: 166.95695265199174
#List Comprehension 177.97208347299602
Curiously on Python 3, I see filter performing faster than list comprehensions.
I always thought that the list comprehensions would be more performant.
Something like:
[name for name in brand_names_db if name is not None]
The bytecode generated is a bit better.
>>> def f1(seq):
... return list(filter(None, seq))
>>> def f2(seq):
... return [i for i in seq if i is not None]
>>> disassemble(f1.__code__)
2 0 LOAD_GLOBAL 0 (list)
2 LOAD_GLOBAL 1 (filter)
4 LOAD_CONST 0 (None)
6 LOAD_FAST 0 (seq)
8 CALL_FUNCTION 2
10 CALL_FUNCTION 1
12 RETURN_VALUE
>>> disassemble(f2.__code__)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0x10cfcaa50, file "<stdin>", line 2>)
2 LOAD_CONST 2 ('f2.<locals>.<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_FAST 0 (seq)
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE