Вопрос-Ответ

How can I bind arguments to a function in Python?

Как я могу привязать аргументы к функции в Python?

Как я могу привязать аргументы к функции 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, используя один из методов, описанных здесь. Смотрите, например, Как передавать аргументы команде Button в Tkinter? и Использование словаря в качестве оператора switch в Python.

Однако некоторые API позволяют вам передавать аргументы, подлежащие привязке, отдельно и будут выполнять привязку за вас. Примечательно, что threading API в стандартной библиотеке работает таким образом. Смотрите, поток запускается перед вызовом Thread.start. Если вы пытаетесь настроить свой собственный API подобным образом, смотрите Как я могу написать простую функцию обратного вызова?.

Явная привязка аргументов также является способом избежать проблем, вызванных поздней привязкой при использовании замыканий. Это проблема, когда, например, lambda внутри for цикла или понимания списка создаются отдельные функции, которые вычисляют один и тот же результат. Смотрите, что фиксируют замыкания лямбда-функций? и создание функций (или лямбд) в цикле (или понимание).

Переведено автоматически
Ответ 1

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)
Ответ 2

Использование 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
Ответ 3

Если 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
Ответ 4

lambdas позволяют создать новую безымянную функцию с меньшим количеством аргументов и вызвать функцию:

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