如何functools部分做它?(How does functools partial do wha

2019-07-21 07:41发布

我不能让我的头就如何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

现在,在该行

incr = lambda y : sum(1, y)

我得到的,不管我的说法传递给incr它将作为传递ylambda将返回sum(1, y)1 + y

我明白那个。 但我不明白这incr2(4)

如何在4被作为通过x在部分功能? 对我来说, 4应更换sum2 。 之间是什么关系x4

Answer 1:

粗略地说, partial做这样的事情(除了关键字ARGS支持等):

def partial(func, *part_args):
    def wrapper(*extra_args):
        args = list(part_args)
        args.extend(extra_args)
        return func(*args)

    return wrapper

因此,通过调用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)

但是你已经有需求的功能,访问一些第三context对象来完成其工作:

def log_event(context, event, params):
    context.log_event("Something happened %s, %s", event, params)

因此,有几种解决方案:

自定义对象:

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)

随着谐音:

context = get_context()  # whatever
notifier.add_listener(partial(log_event, context))

这三个的, partial是最短,最快的。 (对于更复杂的情况下,你可能想要自定义对象虽然)。



Answer 2:

谐音是非常有用的。

例如,在函数调用一个“管内衬”序列(其中从一个函数返回值传递给下一个的参数)。

有时在这样的管道的功能,需要一个参数 ,但功能立即从它的上游返回两个值

在这种情况下, 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)

要通过从目标,你想这样做,当然什么距离排序,这个数据是这样的:

data.sort(key=euclid_dist)

但你不可阻挡- 排序方法的关键参数只接受需要一个参数的功能。

所以重新写euclid_dist作为函数取一个参数:

from functools import partial

p_euclid_dist = partial(euclid_dist, target)

p_euclid_dist现在接受一个参数,

>>> p_euclid_dist((3, 3))
  1.4142135623730951

所以现在你可以通过在排序方法的关键参数的部分功能,您的数据进行排序:

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

或者例如,功能的参数之一,外环变化,但在内部循环迭代过程中是固定的。 通过使用部分,你没有内部循环的迭代过程中额外的参数来传递,因为修改后的(部分)功能并不需要它。

>>> from functools import partial

>>> def fnx(a, b, c):
      return a + b + c

>>> fnx(3, 4, 5)
      12

创建部分功能(使用关键字ARG)

>>> pfnx = partial(fnx, a=12)

>>> pfnx(b=4, c=5)
     21

你还可以创建一个局部的功能与位置参数

>>> pfnx = partial(fnx, 12)

>>> pfnx(4, 5)
      21

但是这将引发(例如,与然后关键字参数使用位置参数调用创建部分)

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

另一种使用情况:使用Python的编写分布式代码multiprocessing库。 的处理池使用池方法创建:

>>> import multiprocessing as MP

>>> # create a process pool:
>>> ppool = MP.Pool()

Pool有一个地图的方法,但只需要一个单一的迭代,所以如果你需要在一个较长的参数列表一个函数时,重新定义功能的部分,要解决所有,但一个:

>>> ppool.map(pfnx, [4, 6, 7, 8])


Answer 3:

分音可以被用于使新的派生的功能具有一些输入参数和预分配的

要查看谐音的一些现实世界中的使用,请参考本真的好博客文章:
http://chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/

一个简单而整洁的初学者例如,从博客,涵盖了一个可以如何使用partialre.search使代码更易读。 re.search方法的签名是:

search(pattern, string, flags=0) 

通过应用partial ,我们可以创建正则表达式的多个版本search ,以满足我们的要求,因此,例如:

is_spaced_apart = partial(re.search, '[a-zA-Z]\s\=')
is_grouped_together = partial(re.search, '[a-zA-Z]\=')

现在is_spaced_apartis_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

这是你如何可以再使用上的一些文字,这些部分功能:

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()

您可以参考上面的链接在主题的深入了解,以获得更,因为它涵盖了这个具体的例子和更多..



Answer 4:

简短的回答, 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


Answer 5:

在我看来,这是实现方式讨好的蟒蛇。

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

其结果是图3和4。



文章来源: How does functools partial do what it does?