如何将参数绑定到 Python 中的函数?

发布于 2024-07-09 14:28:59 字数 1139 浏览 3 评论 0原文

如何将参数绑定到 Python 函数,以便稍后可以在不使用参数(或使用较少的附加参数)的情况下调用它?

例如:

def add(x, y):
    return x + y

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

我这里需要什么magic_function


在框架和库中,人们经常会在尝试向回调提供参数时意外地立即调用函数:例如 on_event(action(foo))。 解决方案是使用此处描述的技术之一将 foo 作为参数绑定到 action。 例如,请参阅 如何将参数传递给 Tkinter 中的 Button 命令?在 Python 中使用字典作为 switch 语句

但是,某些 API 允许您单独传递要绑定的参数,并执行绑定为你。 值得注意的是,标准库中的线程 API 就是这样工作的。 请参阅线程在调用 Thread.start 之前开始运行。 如果您尝试像这样设置自己的 API,请参阅如何编写简单的回调函数?

显式绑定参数也是避免使用闭包时后期绑定引起问题的一种方法。 例如,for 循环或列表理解中的 lambda 会产生计算相同结果的单独函数,从而产生此问题。 请参阅 lambda 函数闭包捕获什么?在循环(或推导式)中创建函数(或 lambda)

How can I bind arguments to a Python function so that I can call it later without arguments (or with fewer additional arguments)?

For example:

def add(x, y):
    return x + y

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

What is the magic_function I need here?


It often happens with frameworks and libraries that people accidentally call a function immediately when trying to give arguments to a callback: for example on_event(action(foo)). The solution is to bind foo as an argument to action, using one of the techniques described here. See for example How to pass arguments to a Button command in Tkinter? and Using a dictionary as a switch statement in Python.

Some APIs, however, allow you to pass the to-be-bound arguments separately, and will do the binding for you. Notably, the threading API in the standard library works this way. See thread starts running before calling Thread.start. If you are trying to set up your own API like this, see How can I write a simple callback function?.

Explicitly binding arguments is also a way to avoid problems caused by late binding when using closures. This is the problem where, for example, a lambda inside a for loop or list comprehension produces separate functions that compute the same result. See What do lambda function closures capture? and Creating functions (or lambdas) in a loop (or comprehension).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

染年凉城似染瑾 2024-07-16 14:28:59

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

import sys
import functools

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

print_hello()
Hello world

上面的用法相当于下面的lambda

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

functools.partial returns a callable wrapping a function with some or all of the arguments frozen.

import sys
import functools

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

print_hello()
Hello world

The above usage is equivalent to the following lambda.

print_hello = lambda *a, **kw: sys.stdout.write("Hello world\n", *a, **kw)
撞了怀 2024-07-16 14:28:59

使用 functools.partial

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

Using functools.partial:

>>> 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
彡翼 2024-07-16 14:28:59

如果 functools.partial 不可用,那么可以轻松模拟它:

>>> make_printer = lambda s: lambda: sys.stdout.write("%s\n" % s)
>>> import sys
>>> print_hello = make_printer("hello")
>>> print_hello()
hello

或者

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

If functools.partial is not available then it can be easily emulated:

>>> make_printer = lambda s: lambda: sys.stdout.write("%s\n" % s)
>>> import sys
>>> print_hello = make_printer("hello")
>>> print_hello()
hello

Or

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
嗫嚅 2024-07-16 14:28:59

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

>>> def foobar(x, y, z):
...     print(f'{x}, {y}, {z}')
... 
>>> foobar(1, 2, 3)  # call normal function
1, 2, 3
>>> bind = lambda x: foobar(x, 10, 20)  # bind 10 and 20 to foobar
>>> bind(1)
1, 10, 20
>>> bind = lambda: foobar(1, 2, 3)  # bind all elements
>>> bind()
1, 2, 3

您还可以使用 functools.partial。 如果您计划在函数调用中使用命名参数绑定,这也适用:

>>> from functools import partial
>>> barfoo = partial(foobar, x=10)
>>> barfoo(y=5, z=6)
10, 5, 6

请注意,如果您从左侧绑定参数,则需要按名称调用参数。 如果从右侧绑定,它会按预期工作。

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

lambdas allow you to create a new unnamed function with fewer arguments and call the function:

>>> def foobar(x, y, z):
...     print(f'{x}, {y}, {z}')
... 
>>> foobar(1, 2, 3)  # call normal function
1, 2, 3
>>> bind = lambda x: foobar(x, 10, 20)  # bind 10 and 20 to foobar
>>> bind(1)
1, 10, 20
>>> bind = lambda: foobar(1, 2, 3)  # bind all elements
>>> bind()
1, 2, 3

You can also use functools.partial. If you are planning to use named argument binding in the function call this is also applicable:

>>> from functools import partial
>>> barfoo = partial(foobar, x=10)
>>> barfoo(y=5, z=6)
10, 5, 6

Note that if you bind arguments from the left you need to call the arguments by name. If you bind from the right it works as expected.

>>> barfoo(5, 6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foobar() got multiple values for argument 'x'
>>> f = partial(foobar, z=20)
>>> f(1, 1)
1, 1, 20
堇色安年 2024-07-16 14:28:59

这也可以工作:

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

This would work, too:

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
薯片软お妹 2024-07-16 14:28:59

在 Python 中函子可以这样定义。 它们是可调用的对象。 “绑定”仅设置参数值。

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

你可以做类似的事情

x= SomeFunctor( 3.456 )
x( arg2=123 )

y= SomeFunctor( 3.456, 123 )
y()

Functors can be defined this way in Python. They're callable objects. The "binding" merely sets argument values.

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

You can do things like

x= SomeFunctor( 3.456 )
x( arg2=123 )

y= SomeFunctor( 3.456, 123 )
y()
你げ笑在眉眼 2024-07-16 14:28:59

该问题一般询问有关绑定参数的问题,但所有答案都与函数有关。 如果您想知道,partial 也适用于类构造函数(即使用类而不是函数作为第一个参数),这对于工厂类很有用。 您可以按如下方式进行操作:

from functools import partial

class Animal(object):
    def __init__(self, weight, num_legs):
        self.weight = weight
        self.num_legs = num_legs
        
animal_class = partial(Animal, weight=12)
snake = animal_class(num_legs = 0)
print(snake.weight) # prints 12

The question asks generally about binding arguments, but all answers are about functions. In case you are wondering, partial also works with class constructors (i.e. using a class instead of a function as a first argument), which can be useful for factory classes. You can do it as follows:

from functools import partial

class Animal(object):
    def __init__(self, weight, num_legs):
        self.weight = weight
        self.num_legs = num_legs
        
animal_class = partial(Animal, weight=12)
snake = animal_class(num_legs = 0)
print(snake.weight) # prints 12
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文