标签归档:partial-application

Python:为什么functools.partial是必需的?

问题:Python:为什么functools.partial是必需的?

部分应用程序很酷。哪些功能functools.partial提供了lambda无法获得的功能?

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

functools某种方式更有效或更可读吗?

Partial application is cool. What functionality does functools.partial offer that you can’t get through lambdas?

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

Is functools somehow more efficient, or readable?


回答 0

哪些功能functools.partial提供了lambda无法获得的功能?

在额外功能方面并没有太多(但是,请参阅稍后)–旁观者眼中的可读性。
大多数熟悉函数式编程语言的人(尤其是Lisp / Scheme系列的人)看起来都lambda很好–我说“大多数”,绝对不是全部,因为Guido和我肯定是“熟悉”的人(等) 却被认为是lambdaPython中的一种令人眼花an乱的异常……
他为曾经接受过Python而打算将其从Python 3中删除(作为“ Python的小故障”之一)感到re悔。
我对此表示完全支持。(我喜欢lambda Scheme,但是它在Python中有局限性,而且它只是奇怪的方式而没有 与其他语言一起使用,让我的皮肤爬行)。

但是,对于成群的lambda恋人而言并非如此-他们在Python的历史中上演过最接近叛逆的事情之一,直到Guido回溯并决定离开lambda
一些可能的添加functools(以使函数返回常量,标识,等)没有发生(避免显式地复制的更多lambda功能),尽管partial当然仍然存在(这不是完全重复,也不是令人讨厌的)。

请记住,lambda身体仅限于表达,因此有其局限性。例如…:

>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>> 

functools.partial返回的函数装饰有用于自省的属性-它包装的函数,以及其中固定的位置和命名参数。此外,可以直接改写命名的参数(在某种意义上,“固定”在某种意义上是默认设置):

>>> f('23', base=10)
23

因此,如您所见,它绝对不像lambda s: int(s, base=2)!-)那样简单

是的,您可以扭曲您的lambda来为您提供一些帮助-例如,对于关键字覆盖,

>>> f = lambda s, **k: int(s, **dict({'base': 2}, **k))

但我非常希望,即使是最热心的- lambda情人,也不要认为这种恐怖比partial通话更容易理解!-)。由于Python的“主体是单个表达式”的局限性,“属性设置”部分更加困难lambda(加上赋值永远不能成为Python表达式的一部分这一事实)……您最终会“伪造表达式中的赋值”通过将列表理解范围扩展到远远超出其设计限制…:

>>> f = [f for f in (lambda f: int(s, base=2),)
           if setattr(f, 'keywords', {'base': 2}) is None][0]

现在结合命名参数覆盖性,再加上三个属性的设置,到一个单一的表达,并告诉我是多么可读将是…!

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 lambda in 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…:

>>> import functools
>>> f = functools.partial(int, base=2)
>>> f.args
()
>>> f.func
<type 'int'>
>>> f.keywords
{'base': 2}
>>> 

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

Ivan Moore的这些帖子扩展了“ lambda的局限性”和python中的闭包:

Well, here’s an example that shows a difference:

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:


回答 2

在最新版本的Python(> = 2.7)中,您可以pickle使用partial,但不能使用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>

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

在Python 3.3上,它提供了:

lambda creation time 3.1743163756961392
lambda execution time 3.040552701787919
partial creation time 3.514482823352731
partial execution time 1.7113973411608114

这意味着partial需要更多的时间来创建,但是执行的时间却要少得多。这很可能是ars的答案中讨论的早期绑定和后期绑定的效果。

Is functools somehow more efficient..?

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.


回答 4

除了Alex提到的额外功能之外,functools.partial的另一个优点是速度。使用partial,您可以避免构造(和破坏)另一个堆栈框架。

默认情况下,由partial和lambda生成的函数都没有文档字符串(尽管您可以通过以下方式为任何对象设置文档字符串 __doc__)。

您可以在此博客中找到更多详细信息:Python中的部分函数应用程序

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

You can find more details in this blog: Partial Function Application in Python


回答 5

我在第三个示例中最快地了解了意图。

当我解析lambda时,我期望比直接由标准库提供的复杂性/奇数更高。

另外,您会注意到,第三个示例是唯一一个不依赖于sum2; 的完整签名的示例。因此使其耦合松散一些。

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.


functools部分如何做?

问题: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

现在排队

incr = lambda y : sum(1, y)

我知道我传递给incr它的任何参数都将传递ylambda哪个参数,sum(1, y)即返回1 + y

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

如何在部分函数中4传递获取x?对我来说,4应该更换sum2x和之间是什么关系4

I am not able to get my head on how the partial works in functools. I have the following code from here:

>>> 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 incr it will be passed as y to lambda which will return sum(1, y) i.e 1 + y.

I understand that. But I didn’t understand this incr2(4).

How does the 4 gets passed as x in partial function? To me, 4 should replace the sum2. What is the relation between x and 4?


回答 0

大致地,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)

至于为什么需要它,有很多情况。仅举一个例子,假设您必须在某个有两个参数的地方传递一个函数:

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

Roughly, partial does something like this (apart from keyword args support etc):

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)

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:

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, partial is the shortest and the fastest. (For a more complex case you might want a custom object though).


回答 1

局部函数非常有用。

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

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

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

partials are 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.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

create a partial function (using keyword 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 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

short answer, partial gives default values to the parameters of a function that would otherwise not have default values.

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

回答 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应用了自变量(因为它patternre.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()

您可以参考上面的链接,以更深入地了解该主题,因为它涵盖了此特定示例以及更多内容。

Partials can be used to make new derived functions that have some input parameters pre-assigned

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/

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:

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

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:

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 link above to get a more in depth understanding of the subject, as it covers this specific example and much more..


回答 4

我认为,这是在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))

结果是3和4。

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

The result is 3 and 4.


回答 5

还值得一提的是,当部分函数传递了另一个我们要“硬编码”某些参数的函数时,该参数应该是最右边的参数。

def func(a,b):
    return a*b
prt = partial(func, b=7)
    print(prt(4))
#return 28

但是,如果我们执行相同的操作,而是改为更改参数

def func(a,b):
    return a*b
 prt = partial(func, a=7)
    print(prt(4))

它将引发错误,“ TypeError:func()为参数’a’获得了多个值”

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


回答 6

这个答案更多是示例代码。上面的所有答案都很好地解释了为什么应该部分使用。我将给出我的观察和有关局部的用例。

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

以上代码的输出应为:

a:1,b:2,c:3
6

注意,在上面的示例中,返回了一个新的callable,它将参数(c)作为其参数。请注意,它也是函数的最后一个参数。

args = [1,2]
partial_adder = partial(adder,*args)
partial_adder(3)

上面代码的输出也是:

a:1,b:2,c:3
6

请注意,*用于解压缩非关键字参数,而返回的callable可以接受的参数与上面相同。

另一个观察结果是: 下面的示例演示了partial返回一个callable,它将以未声明的参数(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)

以上代码的输出应为:

a:20,b:10,c:2,d:3,e:4
39

同样,

kwargs = {'b':10,'c':2}
partial_adder = partial(adder,**kwargs)
partial_adder(20)

以上代码打印

a:20,b:10,c:2,d:3,e:4
39

当我使用模块中的Pool.map_async方法时,我不得不使用它multiprocessing。您只能将一个参数传递给worker函数,因此我不得不使用它partial来使我的worker函数看起来像只有一个输入参数的可调用对象,但实际上我的worker函数具有多个输入参数。

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.

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.

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