Как указать позицию arg для functool part () - PullRequest
3 голосов
/ 02 марта 2020

Согласно руководству, functools partial() используется для частичного применения функции, которая «замораживает» некоторую часть аргументов и / или ключевых слов функции, в результате чего создается новый объект с упрощенной подписью. '

Каков наилучший способ указать позиции аргументов, которые нужно оценить?

РЕДАКТИРОВАТЬ

Обратите внимание, что в комментариях функция, которая должна быть частично оценена, может содержать Именованные и безымянные аргументы (эти функции должны быть совершенно произвольными и могут существовать ранее)

END EDIT

Например, рассмотрим:

def f(x,y,z):
    return x + 2*y + 3*z 

Затем, используя

from functools import partial

и

partial(f,4)(5,6)

и

partial(f,4,5)(6)

, дайте 32.

Но что, если кто-то хочет оценить, скажем третий аргумент z или первый и третий аргументы x и z?

Существует ли удобный способ передачи информации о положении в partial, используя decorator или dict, чьи клавиши являются желаемыми позициями arg и соответствующими v alues ​​являются значениями arg? например, чтобы пройти позиции x и z примерно так:

partial_dict(f,{0:4,2:6})(5)

Ответы [ 3 ]

2 голосов
/ 03 марта 2020

Нет, partial не предназначен для замораживания позиционных аргументов в непоследовательных позициях.

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

def partial_positionals(func, positionals, **keywords):
    def wrapper(*args, **kwargs):
        arg = iter(args)
        return func(*(positionals[i] if i in positionals else next(arg)
            for i in range(len(args) + len(positionals))), **{**keywords, **kwargs})
    return wrapper

так что:

def f(x, y, z):
    return x + 2 * y + 3 * z

print(partial_positionals(f, {0: 4, 2: 6})(5))

выводит:

32
1 голос
/ 03 марта 2020

Просто используйте ключевые аргументы. Используя ваше определение f выше,

>>> g = partial(f, z=10)
>>> g(2, 4)
40
>>> h = partial(f, y=4, z=10)
>>> h(2)
40

Обратите внимание, что как только вы используете аргумент ключевого слова для данного параметра, вы должны использовать аргументы ключевого слова для всех оставшихся аргументов. Например, следующее будет недопустимым:

>>> j = partial(f, x=2, z=10)
>>> j(4)
TypeError: f() got multiple values for argument 'x'

Но продолжать использовать ключевые аргументы:

>>> j = partial(f, x=2, z=10)
>>> j(y=4)
40

Когда вы используете functools.partial, вы сохраняете значения *args и **kwargs для последующей интерполяции. Когда вы позже вызываете «частично примененную» функцию, реализация functools.partial эффективно добавляет ранее предоставленные *args и **kwargs в список аргументов в начале и в конце соответственно, как если бы вы вставили эти распаковки аргументов сами. То есть, вызов

h = partial(1, z=10)
f(4)

примерно эквивалентен записи

args = [1]
kwargs = {'z': 10}
f(*args, 4, **kwargs)

Таким образом, семантика того, как вы предоставляете аргументы для functools.partial, такая же, как для хранения аргументы в переменных args и kwargs выше, так что последний вызов f имеет смысл. Для получения дополнительной информации взгляните на псевдо-реализацию functools.partial, приведенную в документации модуля functools

0 голосов
/ 11 марта 2020

Для более легкого использования вы можете создать новый объект специально для указания позиционного аргумента, который должен быть пропущен при последовательном перечислении значений для позиционных аргументов, которые должны быть зафиксированы с помощью partial:

SKIP = object()

def partial_positionals(func, *positionals, **keywords):
    def wrapper(*args, **kwargs):
        arg = iter(args)
        return func(*(*(next(arg) if i is SKIP else i for i in positionals), *arg),
            **{**keywords, **kwargs})
    return wrapper

, чтобы :

def f(x, y, z):
    return x + 2 * y + 3 * z

print(partial_positionals(f, 4, SKIP, 6)(5))

выходы:

32
...