Python functools partial 如何完成它的工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15331726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does functools partial do what it does?
提问by user1865341
I am not able to get my head on how the partial works in functools. I have the following code from here:
我无法理解 functools 中的部分是如何工作的。我从这里有以下代码:
>>> 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
Now in the line
现在在排队
incr = lambda y : sum(1, y)
I get that whatever argument I pass to incrit will be passed as yto lambdawhich will return sum(1, y)i.e 1 + y.
我知道我传递给incr它的任何参数都将传递y给lambda哪个将返回sum(1, y)ie 1 + y。
I understand that. But I didn't understand this incr2(4).
我明白那个。但我不明白这一点incr2(4)。
How does the 4gets passed as xin partial function? To me, 4should replace the sum2. What is the relation between xand 4?
在部分函数中4获取如何传递x?对我来说,4应该替换sum2. x和之间是什么关系4?
采纳答案by bereal
Roughly, partialdoes something like this (apart from keyword args support etc):
粗略地,partial做这样的事情(除了关键字 args 支持等):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
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)
因此,通过调用partial(sum2, 4)您可以创建一个新函数(准确地说是一个可调用函数),其行为类似于sum2,但少了一个位置参数。那个缺失的参数总是被 替换4,所以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:
至于为什么需要它,有多种情况。仅举一例,假设您必须在预期有 2 个参数的某个地方传递一个函数:
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 contextobject to do its job:
但是你已经拥有的函数需要访问第三个context对象才能完成它的工作:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
So, there are several solutions:
所以,有几种解决方案:
A custom object:
自定义对象:
class Listener(object):
def __init__(self, context):
self._context = context
def __call__(self, event, params):
self._context.log_event("Something happened %s, %s", event, params)
notifier.add_listener(Listener(context))
Lambda:
拉姆达:
log_listener = lambda event, params: log_event(context, event, params)
notifier.add_listener(log_listener)
With partials:
带有部分:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
Of those three, partialis the shortest and the fastest.
(For a more complex case you might want a custom object though).
在这三个中,partial是最短和最快的。(对于更复杂的情况,您可能需要自定义对象)。
回答by doug
partialsare incredibly useful.
部分非常有用。
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.partialmight allow you to keep this function pipeline intact.
在这种情况下,functools.partial可能允许您保持此功能管道完好无损。
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 sortmethod's keyparameter only accepts functions that take a singleargument.
但你不能—— sort方法的key参数只接受带有单个参数的函数。
so re-write euclid_distas a function taking a singleparameter:
所以重写euclid_dist为带有单个参数的函数:
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
p_euclid_distnow accepts a single argument,
p_euclid_dist现在接受一个参数,
>>> 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:
所以现在您可以通过传入 sort 方法的 key 参数的部分函数来对数据进行排序:
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
create a partial function (using keyword arg)
创建一个偏函数(使用关键字 arg)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(b=4, c=5)
21
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 multiprocessinglibrary. A pool of processes is created using the Pool method:
另一个用例:使用 pythonmultiprocessing库编写分布式代码。使用 Pool 方法创建进程池:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Poolhas 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:
Pool有一个 map 方法,但它只需要一个可迭代的,所以如果你需要传入一个具有更长参数列表的函数,请将函数重新定义为部分函数,以修复除一个之外的所有函数:
>>> ppool.map(pfnx, [4, 6, 7, 8])
回答by sisanared
Partials can be used to make new derived functions that have some input parameters pre-assigned
Partials 可用于创建新的派生函数,这些函数具有一些预先分配的输入参数
To see some real world usage of partials, refer to this really good blog post:
http://chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
要查看部分的一些实际用法,请参阅这篇非常好的博客文章:http:
//chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
A simple but neat beginner's example from the blog, covers how one might use partialon re.searchto make code more readable. re.searchmethod's signature is:
博客中一个简单但整洁的初学者示例,涵盖了如何使用partialonre.search使代码更具可读性。 re.search方法的签名是:
search(pattern, string, flags=0)
By applying partialwe can create multiple versions of the regular expression searchto suit our requirements, so for example:
通过应用,partial我们可以创建多个版本的正则表达式search以满足我们的要求,例如:
is_spaced_apart = partial(re.search, '[a-zA-Z]\s\=')
is_grouped_together = partial(re.search, '[a-zA-Z]\=')
Now is_spaced_apartand is_grouped_togetherare two new functions derived from re.searchthat have the patternargument applied(since patternis the first argument in the re.searchmethod's signature).
现在is_spaced_apart和is_grouped_together是从中派生的两个新函数re.search,它们pattern应用了参数(因为pattern是re.search方法签名中的第一个参数)。
The signature of these two new functions(callable) is:
这两个新函数(可调用)的签名是:
is_spaced_apart(string, flags=0) # pattern '[a-zA-Z]\s\=' applied
is_grouped_together(string, flags=0) # pattern '[a-zA-Z]\=' applied
This is how you could then use these partial functions on some text:
这就是您可以在某些文本上使用这些部分函数的方式:
for text in lines:
if is_grouped_together(text):
some_action(text)
elif is_spaced_apart(text):
some_other_action(text)
else:
some_default_action()
You can refer the linkabove to get a more in depth understanding of the subject, as it covers this specific example and much more..
您可以参考上面的链接以更深入地了解该主题,因为它涵盖了此特定示例以及更多内容。
回答by Alex-Antoine Fortin
short answer, partialgives default values to the parameters of a function that would otherwise not have default values.
简短的回答,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
回答by Hanzhou Tang
In my opinion, it's a way to implement curryingin python.
在我看来,这是在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))
The result is 3 and 4.
结果是 3 和 4。
回答by MSK
Also worth to mention, that when partial function passed another function where we want to "hard code" some parameters, that should be rightmost parameter
另外值得一提的是,当部分函数传递另一个我们想要“硬编码”一些参数的函数时,应该是最右边的参数
def func(a,b):
return a*b
prt = partial(func, b=7)
print(prt(4))
#return 28
but if we do the same, but changing a parameter instead
但是如果我们做同样的事情,但是改变一个参数
def func(a,b):
return a*b
prt = partial(func, a=7)
print(prt(4))
it will throw error, "TypeError: func() got multiple values for argument 'a'"
它会抛出错误,“TypeError: func() got multiple values for argument 'a'”
回答by Ruthvik Vaila
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.
请注意,在上面的示例中,返回了一个新的可调用对象,它将参数 (c) 作为它的参数。请注意,它也是该函数的最后一个参数。
args = [1,2]
partial_adder = partial(adder,*args)
partial_adder(3)
Output of the above code is also:
上面代码的输出也是:
a:1,b:2,c:3
6
Notice that * was used to unpack the non-keyword arguments and the callable returned in terms of which argument it can take is same as above.
请注意, * 用于解包非关键字参数,并且根据它可以采用的参数返回的可调用对象与上述相同。
Another observation is:Below example demonstrates that partial returns a callable which will take the undeclared parameter (a) as an argument.
另一个观察结果是:下面的示例演示了 partial 返回一个可调用的,它将未声明的参数 (a) 作为参数。
def adder(a,b=1,c=2,d=3,e=4):
print('a:{},b:{},c:{},d:{},e:{}'.format(a,b,c,d,e))
ans = a+b+c+d+e
print(ans)
partial_adder = partial(adder,b=10,c=2)
partial_adder(20)
Output of the above code should be:
上面代码的输出应该是:
a:20,b:10,c:2,d:3,e:4
39
Similarly,
相似地,
kwargs = {'b':10,'c':2}
partial_adder = partial(adder,**kwargs)
partial_adder(20)
Above code prints
上面的代码打印
a:20,b:10,c:2,d:3,e:4
39
I had to use it when I was using Pool.map_asyncmethod from multiprocessingmodule. You can pass only one argument to the worker function so I had to use partialto make my worker function look like a callable with only one input argument but in reality my worker function had multiple input arguments.
当我使用模块中的Pool.map_async方法时,我不得不使用它multiprocessing。您只能将一个参数传递给工作函数,因此我不得不使用partial它使我的工作函数看起来像一个只有一个输入参数的可调用函数,但实际上我的工作函数有多个输入参数。

