Just as a side note how would the implementation change if there is a dependency between foo and bar. e.g.
foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]
and print:
1.0 [2,3,4,5]
2.0 [1,3,4,5]
3.0 [1,2,4,5]
...
P.S: I know how to do it naively using if, loops and/or generators, but I’d like to learn how to achieve the same using functional tools. Is it just a case of adding an if statement to maptest or apply another filter map to bars internally within maptest?
Are you familiar with other functional languages? i.e. are you trying to learn how python does functional programming, or are you trying to learn about functional programming and using python as the vehicle?
Also, do you understand list comprehensions?
map(f, sequence)
is directly equivalent (*) to:
[f(x) for x in sequence]
In fact, I think map() was once slated for removal from python 3.0 as being redundant (that didn’t happen).
map(f, sequence1, sequence2)
is mostly equivalent to:
[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]
(there is a difference in how it handles the case where the sequences are of different length. As you saw, map() fills in None when one of the sequences runs out, whereas zip() stops when the shortest sequence stops)
So, to address your specific question, you’re trying to produce the result:
foos[0], bars
foos[1], bars
foos[2], bars
# etc.
You could do this by writing a function that takes a single argument and prints it, followed by bars:
def maptest(x):
print x, bars
map(maptest, foos)
Alternatively, you could create a list that looks like this:
[bars, bars, bars, ] # etc.
and use your original maptest:
def maptest(x, y):
print x, y
One way to do this would be to explicitely build the list beforehand:
Alternatively, you could pull in the itertools module. itertools contains many clever functions that help you do functional-style lazy-evaluation programming in python. In this case, we want itertools.repeat, which will output its argument indefinitely as you iterate over it. This last fact means that if you do:
map(maptest, foos, itertools.repeat(bars))
you will get endless output, since map() keeps going as long as one of the arguments is still producing output. However, itertools.imap is just like map(), but stops as soon as the shortest iterable stops.
I’d recommend using a list comprehension (the [(x, bars) for x in foos] part) over using map as it avoids the overhead of a function call on every iteration (which can be very significant). If you’re just going to use it in a for loop, you’ll get better speeds by using a generator comprehension:
>>> y = ((x, bars) for x in foos)
>>> for z in y:
... print z
...
(1.0, [1, 2, 3])
(2.0, [1, 2, 3])
(3.0, [1, 2, 3])
(4.0, [1, 2, 3])
(5.0, [1, 2, 3])
The difference is that the generator comprehension is lazily loaded.
UPDATE In response to this comment:
Of course you know, that you don’t copy bars, all entries are the same bars list. So if you modify any one of them (including original bars), you modify all of them.
I suppose this is a valid point. There are two solutions to this that I can think of. The most efficient is probably something like this:
tbars = tuple(bars)
[(x, tbars) for x in foos]
Since tuples are immutable, this will prevent bars from being modified through the results of this list comprehension (or generator comprehension if you go that route). If you really need to modify each and every one of the results, you can do this:
from copy import copy
[(x, copy(bars)) for x in foos]
However, this can be a bit expensive both in terms of memory usage and in speed, so I’d recommend against it unless you really need to add to each one of them.
Functional programming is about creating side-effect-free code.
map is a functional list transformation abstraction. You use it to take a sequence of something and turn it into a sequence of something else.
You are trying to use it as an iterator. Don’t do that. :)
Here is an example of how you might use map to build the list you want. There are shorter solutions (I’d just use comprehensions), but this will help you understand what map does a bit better:
Notice at this point, you’ve only done a data manipulation. Now you can print it:
for n,l in new_list:
print n, ll
— I’m not sure what you mean by ‘without loops.’ fp isn’t about avoiding loops (you can’t examine every item in a list without visiting each one). It’s about avoiding side-effects, thus writing fewer bugs.
Here’s an overview of the parameters to the map(function, *sequences) function:
function is the name of your function.
sequences is any number of sequences, which are usually lists or tuples. map will iterate over them simultaneously and give the current values to function. That’s why the number of sequences should equal the number of parameters to your function.
It sounds like you’re trying to iterate for some of function‘s parameters but keep others constant, and unfortunately map doesn’t support that. I found an old proposal to add such a feature to Python, but the map construct is so clean and well-established that I doubt something like that will ever be implemented.
Use a workaround like global variables or list comprehensions, as others have suggested.
Does anyone here have any useful code which uses reduce() function in python? Is there any code other than the usual + and * that we see in the examples?
The usage of reduce that I found in my code involved the situation where I had some class structure for logic expression and I needed to convert a list of these expression objects to a conjunction of the expressions. I already had a function make_and to create a conjunction given two expressions, so I wrote reduce(make_and,l). (I knew the list wasn’t empty; otherwise it would have been something like reduce(make_and,l,make_true).)
This is exactly the reason that (some) functional programmers like reduce (or fold functions, as such functions are typically called). There are often already many binary functions like +, *, min, max, concatenation and, in my case, make_and and make_or. Having a reduce makes it trivial to lift these operations to lists (or trees or whatever you got, for fold functions in general).
Of course, if certain instantiations (such as sum) are often used, then you don’t want to keep writing reduce. However, instead of defining the sum with some for-loop, you can just as easily define it with reduce.
Readability, as mentioned by others, is indeed an issue. You could argue, however, that only reason why people find reduce less “clear” is because it is not a function that many people know and/or use.
回答 6
函数组成:如果您已经有了要连续应用的函数列表,例如:
color =lambda x: x.replace('brown','blue')
speed =lambda x: x.replace('quick','slow')
work =lambda x: x.replace('lazy','industrious')
fs =[str.lower, color, speed, work, str.title]
然后,您可以使用以下命令连续应用它们:
>>> call =lambda s, func: func(s)>>> s ="The Quick Brown Fox Jumps Over the Lazy Dog">>> reduce(call, fs, s)'The Slow Blue Fox Jumps Over The Industrious Dog'
>>> call = lambda s, func: func(s)
>>> s = "The Quick Brown Fox Jumps Over the Lazy Dog"
>>> reduce(call, fs, s)
'The Slow Blue Fox Jumps Over The Industrious Dog'
In this case, method chaining may be more readable. But sometimes it isn’t possible, and this kind of composition may be more readable and maintainable than a f1(f2(f3(f4(x)))) kind of syntax.
回答 7
您可以替换value = json_obj['a']['b']['c']['d']['e']为:
value = reduce(dict.__getitem__,'abcde', json_obj)
@Blair Conrad: You could also implement your glob/reduce using sum, like so:
files = sum([glob.glob(f) for f in args], [])
This is less verbose than either of your two examples, is perfectly Pythonic, and is still only one line of code.
So to answer the original question, I personally try to avoid using reduce because it’s never really necessary and I find it to be less clear than other approaches. However, some people get used to reduce and come to prefer it to list comprehensions (especially Haskell programmers). But if you’re not already thinking about a problem in terms of reduce, you probably don’t need to worry about using it.
I’m writing a compose function for a language, so I construct the composed function using reduce along with my apply operator.
In a nutshell, compose takes a list of functions to compose into a single function. If I have a complex operation that is applied in stages, I want to put it all together like so:
Reduce isn’t limited to scalar operations; it can also be used to sort things into buckets. (This is what I use reduce for most often).
Imagine a case in which you have a list of objects, and you want to re-organize it hierarchically based on properties stored flatly in the object. In the following example, I produce a list of metadata objects related to articles in an XML-encoded newspaper with the articles function. articles generates a list of XML elements, and then maps through them one by one, producing objects that hold some interesting info about them. On the front end, I’m going to want to let the user browse the articles by section/subsection/headline. So I use reduce to take the list of articles and return a single dictionary that reflects the section/subsection/article hierarchy.
from lxml import etree
from Reader import Reader
class IssueReader(Reader):
def articles(self):
arts = self.q('//div3') # inherited ... runs an xpath query against the issue
subsection = etree.XPath('./ancestor::div2/@type')
section = etree.XPath('./ancestor::div1/@type')
header_text = etree.XPath('./head//text()')
return map(lambda art: {
'text_id': self.id,
'path': self.getpath(art)[0],
'subsection': (subsection(art)[0] or '[none]'),
'section': (section(art)[0] or '[none]'),
'headline': (''.join(header_text(art)) or '[none]')
}, arts)
def by_section(self):
arts = self.articles()
def extract(acc, art): # acc for accumulator
section = acc.get(art['section'], False)
if section:
subsection = acc.get(art['subsection'], False)
if subsection:
subsection.append(art)
else:
section[art['subsection']] = [art]
else:
acc[art['section']] = {art['subsection']: [art]}
return acc
return reduce(extract, arts, {})
I give both functions here because I think it shows how map and reduce can complement each other nicely when dealing with objects. The same thing could have been accomplished with a for loop, … but spending some serious time with a functional language has tended to make me think in terms of map and reduce.
By the way, if anybody has a better way to set properties like I’m doing in extract, where the parents of the property you want to set might not exist yet, please let me know.
At first glance the following projects use reduce()
MoinMoin
Zope
Numeric
ScientificPython
etc. etc. but then these are hardly surprising since they are huge projects.
The functionality of reduce can be done using function recursion which I guess Guido thought was more explicit.
Update:
Since Google’s Code Search was discontinued on 15-Jan-2012, besides reverting to regular Google searches, there’s something called Code Snippets Collection that looks promising. A number of other resources are mentioned in answers this (closed) question Replacement for Google Code Search?.
Update 2 (29-May-2017):
A good source for Python examples (in open-source code) is the Nullege search engine.
回答 16
import os
files =[# full filenames"var/log/apache/errors.log","home/kane/images/avatars/crusader.png","home/jane/documents/diary.txt","home/kane/images/selfie.jpg","var/log/abc.txt","home/kane/.vimrc","home/kane/images/avatars/paladin.png",]# unfolding of plain filiname list to file-tree
fs_tree =({},# dict of folders[])# list of filesfor full_name in files:
path, fn = os.path.split(full_name)
reduce(# this fucction walks deep into path# and creates placeholders for subfolderslambda d, k: d[0].setdefault(k,# walk deep({},[])),# or create subfolder storage
path.split(os.path.sep),
fs_tree
)[1].append(fn)print fs_tree
#({'home': (# {'jane': (# {'documents': (# {},# ['diary.txt']# )},# []# ),# 'kane': (# {'images': (# {'avatars': (# {},# ['crusader.png',# 'paladin.png']# )},# ['selfie.jpg']# )},# ['.vimrc']# )},# []# ),# 'var': (# {'log': (# {'apache': (# {},# ['errors.log']# )},# ['abc.txt']# )},# [])#},#[])
from collections importCounter
stat2011 =Counter({"January":12,"February":20,"March":50,"April":70,"May":15,"June":35,"July":30,"August":15,"September":20,"October":60,"November":13,"December":50})
stat2012 =Counter({"January":36,"February":15,"March":50,"April":10,"May":90,"June":25,"July":35,"August":15,"September":20,"October":30,"November":10,"December":25})
stat2013 =Counter({"January":10,"February":60,"March":90,"April":10,"May":80,"June":50,"July":30,"August":15,"September":20,"October":75,"November":60,"December":15})
stat_list =[stat2011, stat2012, stat2013]print reduce(lambda x, y: x & y, stat_list)# MINprint reduce(lambda x, y: x | y, stat_list)# MAX
Let say that there are some yearly statistic data stored a list of Counters.
We want to find the MIN/MAX values in each month across the different years.
For example, for January it would be 10. And for February it would be 15.
We need to store the results in a new Counter.
Note that it handles edge cases that popular answer in SO doesn’t. For more in-depth explanation, I am redirecting you to original blog post.
回答 23
使用reduce()来确定日期列表是否连续:
from datetime import date, timedelta
def checked(d1, d2):"""
We assume the date list is sorted.
If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
can advance to the next reduction.
If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
will guarantee the result produced by reduce() to be something other than
the last date in the sorted date list.
Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive
"""#if (d2 - d1).days == 1 or (d2 - d1).days == 0: # for Definition 1if(d2 - d1).days ==1:# for Definition 2return d2
else:return d1 + timedelta(days=-1)# datelist = [date(2014, 1, 1), date(2014, 1, 3),# date(2013, 12, 31), date(2013, 12, 30)]# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),# date(2014, 2, 21), date(2014, 2, 22)]
datelist =[date(2014,2,19), date(2014,2,21),
date(2014,2,22), date(2014,2,20)]
datelist.sort()if datelist[-1]== reduce(checked, datelist):print"dates are consecutive"else:print"dates are not consecutive"
Using reduce() to find out if a list of dates are consecutive:
from datetime import date, timedelta
def checked(d1, d2):
"""
We assume the date list is sorted.
If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
can advance to the next reduction.
If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
will guarantee the result produced by reduce() to be something other than
the last date in the sorted date list.
Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive
"""
#if (d2 - d1).days == 1 or (d2 - d1).days == 0: # for Definition 1
if (d2 - d1).days == 1: # for Definition 2
return d2
else:
return d1 + timedelta(days=-1)
# datelist = [date(2014, 1, 1), date(2014, 1, 3),
# date(2013, 12, 31), date(2013, 12, 30)]
# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),
# date(2014, 2, 21), date(2014, 2, 22)]
datelist = [date(2014, 2, 19), date(2014, 2, 21),
date(2014, 2, 22), date(2014, 2, 20)]
datelist.sort()
if datelist[-1] == reduce(checked, datelist):
print "dates are consecutive"
else:
print "dates are not consecutive"
What is the most idiomatic way to achieve something like the following, in Haskell:
foldl (+) 0 [1,2,3,4,5]
--> 15
Or its equivalent in Ruby:
[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15
Obviously, Python provides the reduce function, which is an implementation of fold, exactly as above, however, I was told that the ‘pythonic’ way of programming was to avoid lambda terms and higher-order functions, preferring list-comprehensions where possible. Therefore, is there a preferred way of folding a list, or list-like structure in Python that isn’t the reduce function, or is reduce the idiomatic way of achieving this?
The Pythonic way of summing an array is using sum. For other purposes, you can sometimes use some combination of reduce (from the functools module) and the operator module, e.g.:
Be aware that reduce is actually a foldl, in Haskell terms. There is no special syntax to perform folds, there’s no builtin foldr, and actually using reduce with non-associative operators is considered bad style.
Using higher-order functions is quite pythonic; it makes good use of Python’s principle that everything is an object, including functions and classes. You are right that lambdas are frowned upon by some Pythonistas, but mostly because they tend not to be very readable when they get complex.
Obviously, that is a trivial example to illustrate a point. In Python you would just do sum([1,2,3,4,5]) and even Haskell purists would generally prefer sum [1,2,3,4,5].
For non-trivial scenarios when there is no obvious convenience function, the idiomatic pythonic approach is to explicitly write out the for loop and use mutable variable assignment instead of using reduce or a fold.
That is not at all the functional style, but that is the “pythonic” way. Python is not designed for functional purists. See how Python favors exceptions for flow control to see how non-functional idiomatic python is.
On the other hand, the documentation expresses preference towards for-loop instead of reduce, hence:
def product(xs):
result = 1
for i in xs:
result *= i
return result
回答 3
您也可以重新发明轮子:
def fold(f, l, a):"""
f: the function to apply
l: the list to fold
a: the accumulator, who is also the 'zero' on the first call
"""return a if(len(l)==0)else fold(f, l[1:], f(a, l[0]))print"Sum:", fold(lambda x, y : x+y,[1,2,3,4,5],0)print"Any:", fold(lambda x, y : x or y,[False,True,False],False)print"All:", fold(lambda x, y : x and y,[False,True,False],True)# Prove that result can be of a different type of the list's elementsprint"Count(x==True):",print fold(lambda x, y : x+1if(y)else x,[False,True,True],0)
def fold(f, l, a):
"""
f: the function to apply
l: the list to fold
a: the accumulator, who is also the 'zero' on the first call
"""
return a if(len(l) == 0) else fold(f, l[1:], f(a, l[0]))
print "Sum:", fold(lambda x, y : x+y, [1,2,3,4,5], 0)
print "Any:", fold(lambda x, y : x or y, [False, True, False], False)
print "All:", fold(lambda x, y : x and y, [False, True, False], True)
# Prove that result can be of a different type of the list's elements
print "Count(x==True):",
print fold(lambda x, y : x+1 if(y) else x, [False, True, True], 0)
Starting Python 3.8, and the introduction of assignment expressions (PEP 572) (:= operator), which gives the possibility to name the result of an expression, we can use a list comprehension to replicate what other languages call fold/foldleft/reduce operations:
Given a list, a reducing function and an accumulator:
items = [1, 2, 3, 4, 5]
f = lambda acc, x: acc * x
accumulator = 1
we can fold items with f in order to obtain the resulting accumulation:
[accumulator := f(accumulator, x) for x in items]
# accumulator = 120
or in a condensed formed:
acc = 1; [acc := acc * x for x in [1, 2, 3, 4, 5]]
# acc = 120
Note that this is actually also a “scanleft” operation as the result of the list comprehension represents the state of the accumulation at each step:
acc = 1
scanned = [acc := acc * x for x in [1, 2, 3, 4, 5]]
# scanned = [1, 2, 6, 24, 120]
# acc = 120
回答 6
对这个(减少)问题的实际答案是:只需使用一个循环!
initial_value =0for x in the_list:
initial_value += x #or any function.
I believe some of the respondents of this question have missed the broader implication of the fold function as an abstract tool. Yes, sum can do the same thing for a list of integers, but this is a trivial case. fold is more generic. It is useful when you have a sequence of data structures of varying shape and want to cleanly express an aggregation. So instead of having to build up a for loop with an aggregate variable and manually recompute it each time, a fold function (or the Python version, which reduce appears to correspond to) allows the programmer to express the intent of the aggregation much more plainly by simply providing two things:
A default starting or “seed” value for the aggregation.
A function that takes the current value of the aggregation (starting with the “seed”) and the next element in the list, and returns the next aggregation value.
I may be quite late to the party, but we can create custom foldr using simple lambda calculus and curried function. Here is my implementation of foldr in python.
def foldr(func):
def accumulator(acc):
def listFunc(l):
if l:
x = l[0]
xs = l[1:]
return func(x)(foldr(func)(acc)(xs))
else:
return acc
return listFunc
return accumulator
def curried_add(x):
def inner(y):
return x + y
return inner
def curried_mult(x):
def inner(y):
return x * y
return inner
print foldr(curried_add)(0)(range(1, 6))
print foldr(curried_mult)(1)(range(1, 6))
Even though the implementation is recursive (might be slow), it will print the values 15 and 120 respectively
>>> sum =lambda x, y : x + y
>>> sum(1,2)3>>> incr =lambda y : sum(1, y)>>> incr(2)3>>>def sum2(x, y):return x + y
>>> incr2 = functools.partial(sum2,1)>>> incr2(4)5
What functionality does functools.partial offer that you can’t get through lambdas?
Not much in terms of extra functionality (but, see later) – and, readability is in the eye of the beholder.
Most people who are familiar with functional programming languages (those in the Lisp/Scheme families in particular) appear to like lambda just fine – I say “most”, definitely not all, because Guido and I assuredly are among those “familiar with” (etc) yet think of lambda as an eyesore anomaly in Python…
He was repentant of ever having accepted it into Python whereas planned to remove it from Python 3, as one of “Python’s glitches”.
I fully supported him in that. (I love lambdain Scheme… while its limitations in Python, and the weird way it just doesn’t fit in with the rest of the language, make my skin crawl).
Not so, however, for the hordes of lambda lovers — who staged one of the closest things to a rebellion ever seen in Python’s history, until Guido backtracked and decided to leave lambda in.
Several possible additions to functools (to make functions returning constants, identity, etc) didn’t happen (to avoid explicitly duplicating more of lambda‘s functionality), though partial did of course remain (it’s no total duplication, nor is it an eyesore).
Remember that lambda‘s body is limited to be an expression, so it’s got limitations. For example…:
functools.partial‘s returned function is decorated with attributes useful for introspection — the function it’s wrapping, and what positional and named arguments it fixes therein. Further, the named arguments can be overridden right back (the “fixing” is rather, in a sense, the setting of defaults):
>>> f('23', base=10)
23
So, as you see, it’s definely not as simplistic as lambda s: int(s, base=2)!-)
Yes, you could contort your lambda to give you some of this – e.g., for the keyword-overriding,
>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))
but I dearly hope that even the most ardent lambda-lover doesn’t consider this horror more readable than the partial call!-). The “attribute setting” part is even harder, because of the “body’s a single expression” limitation of Python’s lambda (plus the fact that assignment can never be part of a Python expression)… you end up “faking assignments within an expression” by stretching list comprehension well beyond its design limits…:
>>> f = [f for f in (lambda f: int(s, base=2),)
if setattr(f, 'keywords', {'base': 2}) is None][0]
Now combine the named-arguments overridability, plus the setting of three attributes, into a single expression, and tell me just how readable that is going to be…!
回答 1
好吧,这是一个显示差异的示例:
In[132]: sum =lambda x, y: x + y
In[133]: n =5In[134]: incr =lambda y: sum(n, y)In[135]: incr2 = partial(sum, n)In[136]:print incr(3), incr2(3)88In[137]: n =9In[138]:print incr(3), incr2(3)128
In [132]: sum = lambda x, y: x + y
In [133]: n = 5
In [134]: incr = lambda y: sum(n, y)
In [135]: incr2 = partial(sum, n)
In [136]: print incr(3), incr2(3)
8 8
In [137]: n = 9
In [138]: print incr(3), incr2(3)
12 8
These posts by Ivan Moore expand on the “limitations of lambda” and closures in python:
>>> pickle.dumps(partial(int))'cfunctools\npartial\np0\n(c__builtin__\nint\np1\ntp2\nRp3\n(g1\n(tNNtp4\nb.'>>> pickle.dumps(lambda x: int(x))Traceback(most recent call last):File"<ipython-input-11-e32d5a050739>", line 1,in<module>
pickle.dumps(lambda x: int(x))File"/usr/lib/python2.7/pickle.py", line 1374,in dumps
Pickler(file, protocol).dump(obj)File"/usr/lib/python2.7/pickle.py", line 224,in dump
self.save(obj)File"/usr/lib/python2.7/pickle.py", line 286,in save
f(self, obj)# Call unbound method with explicit selfFile"/usr/lib/python2.7/pickle.py", line 748,in save_global
(obj, module, name))PicklingError:Can't pickle <function <lambda> at 0x1729aa0>: it's not found as __main__.<lambda>
In the latest versions of Python (>=2.7), you can pickle a partial, but not a lambda:
>>> pickle.dumps(partial(int))
'cfunctools\npartial\np0\n(c__builtin__\nint\np1\ntp2\nRp3\n(g1\n(tNNtp4\nb.'
>>> pickle.dumps(lambda x: int(x))
Traceback (most recent call last):
File "<ipython-input-11-e32d5a050739>", line 1, in <module>
pickle.dumps(lambda x: int(x))
File "/usr/lib/python2.7/pickle.py", line 1374, in dumps
Pickler(file, protocol).dump(obj)
File "/usr/lib/python2.7/pickle.py", line 224, in dump
self.save(obj)
File "/usr/lib/python2.7/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.7/pickle.py", line 748, in save_global
(obj, module, name))
PicklingError: Can't pickle <function <lambda> at 0x1729aa0>: it's not found as __main__.<lambda>
回答 3
functools在某种程度上更有效吗?
作为对此的部分回答,我决定测试性能。这是我的示例:
from functools import partial
import time, math
def make_lambda():
x =1.3returnlambda: math.sin(x)def make_partial():
x =1.3return partial(math.sin, x)Iter=10**7
start = time.clock()for i in range(0,Iter):
l = make_lambda()
stop = time.clock()print('lambda creation time {}'.format(stop - start))
start = time.clock()for i in range(0,Iter):
l()
stop = time.clock()print('lambda execution time {}'.format(stop - start))
start = time.clock()for i in range(0,Iter):
p = make_partial()
stop = time.clock()print('partial creation time {}'.format(stop - start))
start = time.clock()for i in range(0,Iter):
p()
stop = time.clock()print('partial execution time {}'.format(stop - start))
在Python 3.3上,它提供了:
lambda creation time 3.1743163756961392
lambda execution time 3.040552701787919
partial creation time 3.514482823352731
partial execution time 1.7113973411608114
As a partly answer to this I decided to test the performance. Here is my example:
from functools import partial
import time, math
def make_lambda():
x = 1.3
return lambda: math.sin(x)
def make_partial():
x = 1.3
return partial(math.sin, x)
Iter = 10**7
start = time.clock()
for i in range(0, Iter):
l = make_lambda()
stop = time.clock()
print('lambda creation time {}'.format(stop - start))
start = time.clock()
for i in range(0, Iter):
l()
stop = time.clock()
print('lambda execution time {}'.format(stop - start))
start = time.clock()
for i in range(0, Iter):
p = make_partial()
stop = time.clock()
print('partial creation time {}'.format(stop - start))
start = time.clock()
for i in range(0, Iter):
p()
stop = time.clock()
print('partial execution time {}'.format(stop - start))
on Python 3.3 it gives:
lambda creation time 3.1743163756961392
lambda execution time 3.040552701787919
partial creation time 3.514482823352731
partial execution time 1.7113973411608114
Which means that partial needs a bit more time for creation but considerably less time for execution. This can well be the effect of the early and late binding which are discussed in the answer from ars.
Besides the extra functionality Alex mentioned, another advantage of functools.partial is speed. With partial you can avoid constructing (and destructing) another stack frame.
Neither the function generated by partial nor lambdas have docstrings by default (though you can set the doc string for any objects via __doc__).
I understand the intent quickest in the third example.
When I parse lambdas, I’m expecting more complexity/oddity than offered by the standard library directly.
Also, you’ll notice that the third example is the only one which doesn’t depend on the full signature of sum2; thus making it slightly more loosely coupled.
>>> sum =lambda x, y : x + y
>>> sum(1,2)3>>> incr =lambda y : sum(1, y)>>> incr(2)3>>>def sum2(x, y):return x + y
>>> incr2 = functools.partial(sum2,1)>>> incr2(4)5
classEventNotifier(object):def __init__(self):
self._listeners =[]def add_listener(self, callback):''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)# ...def notify(self, event,*params):for f in self._listeners:
f(event, params)
So, by calling partial(sum2, 4) you create a new function (a callable, to be precise) that behaves like sum2, but has one positional argument less. That missing argument is always substituted by 4, so that partial(sum2, 4)(2) == sum2(4, 2)
As for why it’s needed, there’s a variety of cases. Just for one, suppose you have to pass a function somewhere where it’s expected to have 2 arguments:
class EventNotifier(object):
def __init__(self):
self._listeners = []
def add_listener(self, callback):
''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)
# ...
def notify(self, event, *params):
for f in self._listeners:
f(event, params)
But a function you already have needs access to some third context object to do its job:
For instance, in a ‘pipe-lined’ sequence of function calls (in which the returned value from one function is the argument passed to the next).
Sometimes a function in such a pipeline requires a single argument, but the function immediately upstream from it returns two values.
In this scenario, functools.partial might allow you to keep this function pipeline intact.
Here’s a specific, isolated example: suppose you want to sort some data by each data point’s distance from some target:
# create some data
import random as RND
fnx = lambda: RND.randint(0, 10)
data = [ (fnx(), fnx()) for c in range(10) ]
target = (2, 4)
import math
def euclid_dist(v1, v2):
x1, y1 = v1
x2, y2 = v2
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
To sort this data by distance from the target, what you would like to do of course is this:
data.sort(key=euclid_dist)
but you can’t–the sort method’s key parameter only accepts functions that take a single argument.
so re-write euclid_dist as a function taking a single parameter:
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
p_euclid_dist now accepts a single argument,
>>> p_euclid_dist((3, 3))
1.4142135623730951
so now you can sort your data by passing in the partial function for the sort method’s key argument:
data.sort(key=p_euclid_dist)
# verify that it works:
for p in data:
print(round(p_euclid_dist(p), 3))
1.0
2.236
2.236
3.606
4.243
5.0
5.831
6.325
7.071
8.602
Or for instance, one of the function’s arguments changes in an outer loop but is fixed during iteration in the inner loop. By using a partial, you don’t have to pass in the additional parameter during iteration of the inner loop, because the modified (partial) function doesn’t require it.
>>> from functools import partial
>>> def fnx(a, b, c):
return a + b + c
>>> fnx(3, 4, 5)
12
you can also create a partial function with a positional argument
>>> pfnx = partial(fnx, 12)
>>> pfnx(4, 5)
21
but this will throw (e.g., creating partial with keyword argument then calling using positional arguments)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(4, 5)
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
pfnx(4, 5)
TypeError: fnx() got multiple values for keyword argument 'a'
another use case: writing distributed code using python’s multiprocessing library. A pool of processes is created using the Pool method:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Pool has a map method, but it only takes a single iterable, so if you need to pass in a function with a longer parameter list, re-define the function as a partial, to fix all but one:
>>> ppool.map(pfnx, [4, 6, 7, 8])
回答 2
简短的答案,partial为函数的参数提供默认值,否则将没有默认值。
from functools import partial
def foo(a,b):return a+b
bar = partial(foo, a=1)# equivalent to: foo(a=1, b)
bar(b=10)#11 = 1+10
bar(a=101, b=10)#111=101+10
A simple but neat beginner’s example from the blog, covers how one might use partial on re.search to make code more readable. re.search method’s signature is:
search(pattern, string, flags=0)
By applying partial we can create multiple versions of the regular expression search to suit our requirements, so for example:
Now is_spaced_apart and is_grouped_together are two new functions derived from re.search that have the pattern argument applied(since pattern is the first argument in the re.search method’s signature).
The signature of these two new functions(callable) is:
from functools import partial
def add(a,b):return a + b
def add2number(x,y,z):return x + y + z
if __name__ =="__main__":
add2 = partial(add,2)print("result of add2 ",add2(1))
add3 = partial(partial(add2number,1),2)print("result of add3",add3(1))
In my opinion, it’s a way to implement currying in python.
from functools import partial
def add(a,b):
return a + b
def add2number(x,y,z):
return x + y + z
if __name__ == "__main__":
add2 = partial(add,2)
print("result of add2 ",add2(1))
add3 = partial(partial(add2number,1),2)
print("result of add3",add3(1))
Also worth to mention, that when partial function passed another function where we want to “hard code” some parameters, that should be rightmost parameter
from functools import partial
def adder(a,b,c):print('a:{},b:{},c:{}'.format(a,b,c))
ans = a+b+c
print(ans)
partial_adder = partial(adder,1,2)
partial_adder(3)## now partial_adder is a callable that can take only one argument
This answer is more of an example code. All the above answers give good explanations regarding why one should use partial. I will give my observations and use cases about partial.
from functools import partial
def adder(a,b,c):
print('a:{},b:{},c:{}'.format(a,b,c))
ans = a+b+c
print(ans)
partial_adder = partial(adder,1,2)
partial_adder(3) ## now partial_adder is a callable that can take only one argument
Output of the above code should be:
a:1,b:2,c:3
6
Notice that in the above example a new callable was returned that will take parameter (c) as it’s argument. Note that it is also the last argument to the function.
I had to use it when I was using Pool.map_async method from multiprocessing module. You can pass only one argument to the worker function so I had to use partial to make my worker function look like a callable with only one input argument but in reality my worker function had multiple input arguments.
>>>def f(x):return x %2!=0and x %3!=0>>> filter(f, range(2,25))[5,7,11,13,17,19,23]>>>def cube(x):return x*x*x>>> map(cube, range(1,11))[1,8,27,64,125,216,343,512,729,1000]>>>def add(x,y):return x+y>>> reduce(add, range(1,11))55
但是在Python 3中,我收到以下输出:
>>> filter(f, range(2,25))<filter object at 0x0000000002C14908>>>> map(cube, range(1,11))<map object at 0x0000000002C82B70>>>> reduce(add, range(1,11))Traceback(most recent call last):File"<pyshell#8>", line 1,in<module>
reduce(add, range(1,11))NameError: name 'reduce'isnot defined
>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>
>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>
>>> reduce(add, range(1, 11))
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
reduce(add, range(1, 11))
NameError: name 'reduce' is not defined
I would appreciate if someone could explain to me why this is.
map() and filter() return iterators. If you really need a list, a quick fix is e.g. list(map(...)), but a better fix is often to use a list comprehension (especially when the original code uses lambda), or rewriting the code so it doesn’t need a list at all. Particularly tricky is map() invoked for the side effects of the function; the correct transformation is to use a regular for loop (since creating a list would just be wasteful).
>>>def f(x):return x %2!=0and x %3!=0...>>> list(filter(f, range(2,25)))[5,7,11,13,17,19,23]>>>def cube(x):return x*x*x
...>>> list(map(cube, range(1,11)))[1,8,27,64,125,216,343,512,729,1000]>>>import functools
>>>def add(x,y):return x+y
...>>> functools.reduce(add, range(1,11))55>>>
现在的建议是,用生成器表达式或列表推导替换map和filter的用法。例:
>>>def f(x):return x %2!=0and x %3!=0...>>>[i for i in range(2,25)if f(i)][5,7,11,13,17,19,23]>>>def cube(x):return x*x*x
...>>>[cube(i)for i in range(1,11)][1,8,27,64,125,216,343,512,729,1000]>>>
The functionality of map and filter was intentionally changed to return iterators, and reduce was removed from being a built-in and placed in functools.reduce.
So, for filter and map, you can wrap them with list() to see the results like you did before.
As an addendum to the other answers, this sounds like a fine use-case for a context manager that will re-map the names of these functions to ones which return a list and introduce reduce in the global namespace.
A quick implementation might look like this:
from contextlib import contextmanager
@contextmanager
def noiters(*funcs):
if not funcs:
funcs = [map, filter, zip] # etc
from functools import reduce
globals()[reduce.__name__] = reduce
for func in funcs:
globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
try:
yield
finally:
del globals()[reduce.__name__]
for func in funcs: globals()[func.__name__] = func
With a usage that looks like this:
with noiters(map):
from operator import add
print(reduce(add, range(1, 20)))
print(map(int, ['1', '2']))
Since the reduce method has been removed from the built in function from Python3, don’t forget to import the functools in your code. Please look at the code snippet below.
The reduce function, since it is not commonly used, was removed from the built-in functions in Python 3. It is still available in the functools module, so you can do:
def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):return list(
filter(lambda x: x !=[],
map(lambda x: x[0]if city in x[1]else[], flight_destinations_dict.items())))
One of the advantages of map, filter and reduce is how legible they become when you “chain” them together to do something complex. However, the built-in syntax isn’t legible and is all “backwards”. So, I suggest using the PyFunctional package (https://pypi.org/project/PyFunctional/).
Here’s a comparison of the two:
“I have a sequence of flight destinations. Out of which I want to get
the dict key if city is in the dict values. Finally, filter out the
empty lists I created in the process.”
from functional import seq # PyFunctional package to allow easier syntax
def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
return seq(flight_destinations_dict.items()) \
.map(lambda x: x[0] if city in x[1] else []) \
.filter(lambda x: x != []) \
Default Python version
It’s all backwards. You need to say:
“OK, so, there’s a list. I want to filter empty lists out of it. Why?
Because I first got the dict key if the city was in the dict values.
Oh, the list I’m doing this to is flight_destinations_dict.”
def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
return list(
filter(lambda x: x != [],
map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
)
)
I have always thought that functional programming can be done in Python. Thus, I was surprised that Python didn’t get much of a mention in this question, and when it was mentioned, it normally wasn’t very positive. However, not many reasons were given for this (lack of pattern matching and algebraic data types were mentioned). So my question is: why isn’t Python very good for functional programming? Are there more reasons than its lack of pattern matching and algebraic data types? Or are these concepts so important to functional programming that a language that doesn’t support them can only be classed as a second rate functional programming language? (Keep in mind that my experience with functional programming is quite limited.)
The question you reference asks which languages promote both OO and functional programming. Python does not promote functional programming even though it works fairly well.
The best argument against functional programming in Python is that imperative/OO use cases are carefully considered by Guido, while functional programming use cases are not. When I write imperative Python, it’s one of the prettiest languages I know. When I write functional Python, it becomes as ugly and unpleasant as your average language that doesn’t have a BDFL.
Which is not to say that it’s bad, just that you have to work harder than you would if you switched to a language that promotes functional programming or switched to writing OO Python.
No pattern matching and no tail recursion mean your basic algorithms have to be written imperatively. Recursion is ugly and slow in Python.
A small list library and no functional dictionaries mean that you have to write a lot of stuff yourself.
No syntax for currying or composition means that point-free style is about as full of punctuation as explicitly passing arguments.
Iterators instead of lazy lists means that you have to know whether you want efficiency or persistence, and to scatter calls to list around if you want persistence. (Iterators are use-once)
Python’s simple imperative syntax, along with its simple LL1 parser, mean that a better syntax for if-expressions and lambda-expressions is basically impossible. Guido likes it this way, and I think he’s right.
Guido has a good explanation of this here. Here’s the most relevant part:
I have never considered Python to be
heavily influenced by functional
languages, no matter what people say
or think. I was much more familiar
with imperative languages such as C
and Algol 68 and although I had made
functions first-class objects, I
didn’t view Python as a functional
programming language. However, earlier
on, it was clear that users wanted to
do much more with lists and functions.
…
It is also worth noting that even
though I didn’t envision Python as a
functional language, the introduction
of closures has been useful in the
development of many other advanced
programming features. For example,
certain aspects of new-style classes,
decorators, and other modern features
rely upon this capability.
Lastly, even though a number of
functional programming features have
been introduced over the years, Python
still lacks certain features found in
“real” functional programming
languages. For instance, Python does
not perform certain kinds of
optimizations (e.g., tail recursion).
In general, because Python’s extremely
dynamic nature, it is impossible to do
the kind of compile-time optimization
known from functional languages like
Haskell or ML. And that’s fine.
I pull two things out of this:
The language’s creator doesn’t really consider Python to be a functional language. Therefore, it’s possible to see “functional-esque” features, but you’re unlikely to see anything that is definitively functional.
Python’s dynamic nature inhibits some of the optimizations you see in other functional languages. Granted, Lisp is just as dynamic (if not more dynamic) as Python, so this is only a partial explanation.
Scheme doesn’t have algebraic data types or pattern matching but it’s certainly a functional language. Annoying things about Python from a functional programming perspective:
Crippled Lambdas. Since Lambdas can only contain an expression, and you can’t do everything as easily in an expression context, this means that the functions you can define “on the fly” are limited.
Ifs are statements, not expressions. This means, among other things, you can’t have a lambda with an If inside it. (This is fixed by ternaries in Python 2.5, but it looks ugly.)
On the other hand, python has lexical closures, Lambdas, and list comprehensions (which are really a “functional” concept whether or not Guido admits it). I do plenty of “functional-style” programming in Python, but I’d hardly say it’s ideal.
I would never call Python “functional” but whenever I program in Python the code invariably ends up being almost purely functional.
Admittedly, that’s mainly due to the extremely nice list comprehension. So I wouldn’t necessarily suggest Python as a functional programming language but I would suggest functional programming for anyone using Python.
One thing that is really important for this question (and the answers) is the following:
What the hell is functional programming, and what are the most important properties of it.
I’ll try to give my view of it:
Functional programming is a lot like writing math on a whiteboard. When you write equations
on a whiteboard, you do not think about an execution order. There is (typically) no mutation.
You don’t come back the day after and look at it, and when you make the calculations again,
you get a different result (or you may, if you’ve had some fresh coffee :)). Basically,
what is on the board is there, and the answer was already there when you started writing
things down, you just haven’t realized what it is yet.
Functional programming is a lot like that; you don’t change things, you just evaluate
the equation (or in this case, “program”) and figure out what the answer is. The program
is still there, unmodified. The same with the data.
I would rank the following as the most important features of functional programming:
a) referential transparency – if you evaluate the same statement at some other time
and place, but with the same variable values, it will still mean the same.
b) no side effect – no matter how long you stare at the whiteboard, the equation another
guy is looking at at another whiteboard won’t accidentally change.
c) functions are values too. which can be passed around and applied with, or to, other
variables.
d) function composition, you can do h=g·f and thus define a new function h(..) which is
equivalent to calling g(f(..)).
This list is in my prioritized order, so referential transparency is the most important,
followed by no side effects.
Now, if you go through python and check how well the language and libraries supports,
and guarantees, these aspects – then you are well on the way to answer your own question.
Another reason not mentioned above is that many built-in functions and methods of built-in types modify an object but do not return the modified object. If those modified objects were returned, that would make functional code cleaner and more concise. For example, if some_list.append(some_object) returned some_list with some_object appended.
In addition to other answers, one reason Python and most other multi-paradigm languages are not well suited for true functional programming is because their compilers / virtual machines / run-times do not support functional optimization. This sort of optimization is achieved by the compiler understanding mathematical rules. For example, many programming languages support a map function or method. This is a fairly standard function that takes a function as one argument and a iterable as the second argument then applies that function to each element in the iterable.
Anyways it turns out that map( foo() , x ) * map( foo(), y ) is the same as map( foo(), x * y ). The latter case is actually faster than the former because the former performs two copies where the latter performs one.
Better functional languages recognize these mathematically based relationships and automatically perform the optimization. Languages that aren’t dedicated to the functional paradigm will likely not optimize.
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