use*_*341 154 python functional-programming partial-application functools
我无法理解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
Run Code Online (Sandbox Code Playgroud)
现在就行了
incr = lambda y : sum(1, y)
Run Code Online (Sandbox Code Playgroud)
我得到的是,无论我传递给incr
它的任何论点都将被传递y
给lambda
哪个将返回sum(1, y)
ie 1 + y
.
我明白那个.但我不明白这一点incr2(4)
.
如何在部分函数中4
传递x
?对我来说,4
应该更换sum2
.x
和之间有什么关系4
?
ber*_*eal 186
粗略地说,partial
做这样的事情(除了关键字args支持等):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
Run Code Online (Sandbox Code Playgroud)
所以,通过调用partial(sum2, 4)
你创建一个新的函数(一个可调用的,确切地说),其行为类似于sum2
一个位置参数.那个缺失的参数总是被替换4
,所以partial(sum2, 4)(2) == sum2(4, 2)
至于为什么需要,有各种各样的案例.只是为了一个,假设你必须在一个预期有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)
Run Code Online (Sandbox Code Playgroud)
但是你已经拥有的一个函数需要访问一些第三个context
对象来完成它的工作:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
Run Code Online (Sandbox Code Playgroud)
所以,有几种解决方案:
自定义对象:
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))
Run Code Online (Sandbox Code Playgroud)
LAMBDA:
log_listener = lambda event, params: log_event(context, event, params)
notifier.add_listener(log_listener)
Run Code Online (Sandbox Code Playgroud)
有部分:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
Run Code Online (Sandbox Code Playgroud)
在这三者中,partial
是最短和最快的.(对于更复杂的情况,您可能需要自定义对象).
dou*_*oug 79
部分非常有用.
例如,在'pipe-lined'函数调用序列中(其中一个函数的返回值是传递给下一个函数的参数).
有时这种管道中的函数需要一个参数,但紧接其上游的函数返回两个值.
在这种情况下,functools.partial
可能允许您保持此功能管道不变.
这是一个特定的,孤立的示例:假设您希望按每个数据点与某个目标的距离对某些数据进行排序:
# 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)
Run Code Online (Sandbox Code Playgroud)
要按目标距离对数据进行排序,您当然希望这样做:
data.sort(key=euclid_dist)
Run Code Online (Sandbox Code Playgroud)
但你不能 - sort方法的key参数只接受带有一个参数的函数.
所以重写euclid_dist
为带有单个参数的函数:
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
Run Code Online (Sandbox Code Playgroud)
p_euclid_dist
现在接受一个参数,
>>> p_euclid_dist((3, 3))
1.4142135623730951
Run Code Online (Sandbox Code Playgroud)
所以现在你可以通过传入sort方法的key参数的partial函数来对数据进行排序:
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
Run Code Online (Sandbox Code Playgroud)
或者,例如,函数的一个参数在外部循环中更改,但在内部循环的迭代期间是固定的.通过使用partial,您不必在内循环的迭代期间传递附加参数,因为修改的(部分)函数不需要它.
>>> from functools import partial
>>> def fnx(a, b, c):
return a + b + c
>>> fnx(3, 4, 5)
12
Run Code Online (Sandbox Code Playgroud)
创建部分函数(使用关键字arg)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(b=4, c=5)
21
Run Code Online (Sandbox Code Playgroud)
您还可以使用位置参数创建部分函数
>>> pfnx = partial(fnx, 12)
>>> pfnx(4, 5)
21
Run Code Online (Sandbox Code Playgroud)
但这将抛出(例如,创建部分关键字参数然后使用位置参数调用)
>>> 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'
Run Code Online (Sandbox Code Playgroud)
另一个用例:使用python的multiprocessing
库编写分布式代码.使用Pool方法创建进程池:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Run Code Online (Sandbox Code Playgroud)
Pool
有一个map方法,但它只需要一个可迭代的,所以如果你需要传入一个带有较长参数列表的函数,重新定义该函数作为局部函数来修复除一个之外的所有函数:
>>> ppool.map(pfnx, [4, 6, 7, 8])
Run Code Online (Sandbox Code Playgroud)
sis*_*red 29
部分可用于创建预先分配了一些输入参数的新派生函数
要查看部分实际使用情况,请参阅这篇非常好的博文:http:
//chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
博客中一个简单而又整洁的初学者示例,介绍了如何使用partial
它re.search
来使代码更具可读性. re.search
方法的签名是:
search(pattern, string, flags=0)
Run Code Online (Sandbox Code Playgroud)
通过应用,partial
我们可以创建正则表达式的多个版本search
以满足我们的要求,例如:
is_spaced_apart = partial(re.search, '[a-zA-Z]\s\=')
is_grouped_together = partial(re.search, '[a-zA-Z]\=')
Run Code Online (Sandbox Code Playgroud)
现在,is_spaced_apart
和is_grouped_together
有来源于两个新功能re.search
是有pattern
应用参数(因为pattern
是在第一个参数re.search
方法的签名).
这两个新函数(可调用)的签名是:
is_spaced_apart(string, flags=0) # pattern '[a-zA-Z]\s\=' applied
is_grouped_together(string, flags=0) # pattern '[a-zA-Z]\=' applied
Run Code Online (Sandbox Code Playgroud)
这就是你可以在某些文本上使用这些部分函数的方法:
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()
Run Code Online (Sandbox Code Playgroud)
您可以参考上面的链接,以更深入地了解该主题,因为它涵盖了这个具体的例子等等.
Ale*_*tin 26
简短回答,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
Run Code Online (Sandbox Code Playgroud)
我认为,这是在python中实现currying的一种方式。
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))
Run Code Online (Sandbox Code Playgroud)
结果是3和4。