关于部分应用:python参数绑定

Python Argument Binders

如何将参数绑定到python方法以存储空函数以供以后调用?类似于C++的EDCOX1,0Ω。

例如:

1
2
3
4
5
def add(x, y):
    return x + y

add_5 = magic_function(add, 5)
assert add_5(3) == 8

functools.partial返回一个可调用的包装函数,其中部分或全部参数被冻结。

1
2
3
4
5
6
7
import sys
import functools

print_hello = functools.partial(sys.stdout.write,"Hello world
"
)

print_hello()
1
Hello world

上述用法相当于以下lambda

1
2
print_hello = lambda *a, **kw: sys.stdout.write("Hello world
"
, *a, **kw)


我不太熟悉boost::bind,但是来自functoolspartial函数可能是一个好的开始:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from functools import partial

>>> def f(a, b):
...     return a+b

>>> p = partial(f, 1, 2)
>>> p()
3

>>> p2 = partial(f, 1)
>>> p2(7)
8


如果functools.partial不可用,则可以很容易地模拟:

1
2
3
4
5
6
>>> make_printer = lambda s: lambda: sys.stdout.write("%s
"
% s)
>>> import sys
>>> print_hello = make_printer("hello")
>>> print_hello()
hello

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def partial(func, *args, **kwargs):
    def f(*args_rest, **kwargs_rest):
        kw = kwargs.copy()
        kw.update(kwargs_rest)
        return func(*(args + args_rest), **kw)
    return f

def f(a, b):
    return a + b

p = partial(f, 1, 2)
print p() # -> 3

p2 = partial(f, 1)
print p2(7) # -> 8

d = dict(a=2, b=3)
p3 = partial(f, **d)
print p3(), p3(a=3), p3() # -> 5 6 5

lambda允许您用更少的参数创建一个新的未命名函数,并调用该函数!

1
2
3
4
5
6
7
8
9
>>> def foobar(x,y,z):
...     print"%d, %d, %d" % (x,y,z)
>>> foobar(1,2,3) # call normal function

>>> bind = lambda x: foobar(x, 10, 20) # bind 10 and 20 to foobar
>>> bind(1) # print 1, 10, 20

>>> bind = lambda: foobar(1,2,3) # bind all elements  
>>> bind()  # print 1, 2, 3

编辑

https://docs.python.org/2/library/functools.html functools.partial

如果计划在函数调用中使用命名参数绑定,这也适用于:

1
2
3
4
>>> from functools import partial
>>> barfoo = partial(foobar, x=10)
>>> barfoo(y=5,z=6)
21

尽管如此,请注意

1
2
3
4
5
6
7
>>> barfoo(5,6)
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
TypeError: foobar() got multiple values for keyword argument 'x'
>>> f = partial(foobar, z=20)
>>> f(1,1)
22


这也会起作用:

1
2
3
4
5
6
7
8
9
def curry(func, *args):
    def curried(*innerargs):
       return func(*(args+innerargs))
    curried.__name__ ="%s(%s, ...)" % (func.__name__,",".join(map(str, args)))
    return curried

>>> w=curry(sys.stdout.write,"Hey there")
>>> w()
Hey there

函数可以用这种方式在Python中定义。它们是可调用的对象。"binding"只设置参数值。

1
2
3
4
5
6
7
8
9
class SomeFunctor( object ):
    def __init__( self, arg1, arg2=None ):
        self.arg1= arg1
        self.arg2= arg2
    def __call___( self, arg1=None, arg2=None ):
        a1= arg1 or self.arg1
        a2= arg2 or self.arg2
        # do something
        return

你可以做像

1
2
3
4
5
x= SomeFunctor( 3.456 )
x( arg2=123 )

y= SomeFunctor( 3.456, 123 )
y()