为了账号安全,请及时绑定邮箱和手机立即绑定

functools部分如何做?

functools部分如何做?

慕无忌1623718 2019-12-07 15:02:44
我无法了解部分功能在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它的任何参数都将传递y给lambda哪个参数,sum(1, y)即返回1 + y。我明白那个。但是我不明白incr2(4)。如何在部分函数中4传递获取x?对我来说,4应该更换sum2。x和之间是什么关系4?
查看完整描述

3 回答

?
RISEBY

TA贡献1856条经验 获得超5个赞

大致地,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最短和最快。(对于更复杂的情况,您可能需要自定义对象)。


查看完整回答
反对 回复 2019-12-07
?
一只甜甜圈

TA贡献1836条经验 获得超5个赞

局部函数非常有用。


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


有时,这样的管道中的函数需要单个参数,但是紧接其上游的函数返回两个值。


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

因此,现在您可以通过传递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

又例如,函数的参数之一在外循环中更改,但在内循环的迭代过程中是固定的。通过使用部分函数,您无需在内部循环的迭代过程中传递其他参数,因为修改后的(部分函数)不需要此参数。


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

但这会抛出(例如,创建带有关键字参数的partial,然后使用位置参数调用)


>>> 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库编写分布式代码。使用Pool方法创建一个进程池:


>>> import multiprocessing as MP


>>> # create a process pool:

>>> ppool = MP.Pool()

Pool 有一个map方法,但是它只需要一个可迭代的方法,因此,如果您需要传入带有较长参数列表的函数,请将该函数重新定义为局部函数,以修复除一个以外的所有函数:


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


查看完整回答
反对 回复 2019-12-07
  • 3 回答
  • 0 关注
  • 458 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信