Подтвердить что ты не робот

Можно ли частично применить второй аргумент функции, которая не принимает аргументы с ключевым словом?

Возьмем, к примеру, питон, встроенный в функцию pow().

xs = [1,2,3,4,5,6,7,8]

from functools import partial

list(map(partial(pow,2),xs))

>>> [2, 4, 8, 16, 32, 128, 256]

но как бы я поднять xs до степени 2?

чтобы получить [1, 4, 9, 16, 25, 49, 64]

list(map(partial(pow,y=2),xs))

TypeError: pow() takes no keyword arguments

Я знаю, что понимание списков будет проще.

4b9b3361

Ответ 1

Нет

Согласно документации, partial не может этого сделать (выделение мое собственное):

partial.args

Самые левые позиционные аргументы, которые будут добавлены к позиционным аргументам


Вы всегда можете просто "исправить" pow, чтобы иметь ключевое слово args:

_pow = pow
pow = lambda x, y: _pow(x, y)

Ответ 2

Думаю, я просто использовал бы этот простой однострочный слой:

import itertools
print list(itertools.imap(pow, [1, 2, 3], itertools.repeat(2)))

Update:

Я также придумал смешнее, чем полезное решение. Это красивый синтаксический сахар, который выгоден из-за того, что литерал ... означает Ellipsis в Python3. Это модифицированная версия partial, позволяющая опустить некоторые позиционные аргументы между самой левой и самой правой. Единственный недостаток заключается в том, что вы больше не можете передавать Ellipsis в качестве аргумента.

import itertools
def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(newfunc.leftmost_args + fargs + newfunc.rightmost_args), **newkeywords)
    newfunc.func = func
    args = iter(args)
    newfunc.leftmost_args = tuple(itertools.takewhile(lambda v: v != Ellipsis, args))
    newfunc.rightmost_args = tuple(args)
    newfunc.keywords = keywords
    return newfunc

>>> print partial(pow, ..., 2, 3)(5) # (5^2)%3
1
>>> print partial(pow, 2, ..., 3)(5) # (2^5)%3
2
>>> print partial(pow, 2, 3, ...)(5) # (2^3)%5
3
>>> print partial(pow, 2, 3)(5) # (2^3)%5
3

Итак, решение для исходного вопроса будет с этой версией частичного list(map(partial(pow, ..., 2),xs))

Ответ 3

Для этого вы можете создать вспомогательную функцию:

from functools import wraps
def foo(a, b, c, d, e):
    print('foo(a={}, b={}, c={}, d={}, e={})'.format(a, b, c, d, e))

def partial_at(func, index, value):
    @wraps(func)
    def result(*rest, **kwargs):
        args = []
        args.extend(rest[:index])
        args.append(value)
        args.extend(rest[index:])
        return func(*args, **kwargs)
    return result

if __name__ == '__main__':
    bar = partial_at(foo, 2, 'C')
    bar('A', 'B', 'D', 'E') 
    # Prints: foo(a=A, b=B, c=C, d=D, e=E)

Отказ от ответственности: я не тестировал это с помощью аргументов ключевого слова, чтобы он мог как-то взорваться из-за них. Кроме того, я не уверен, что это то, что нужно использовать @wraps, но это казалось правдой.

Ответ 4

вы можете использовать закрытие

xs = [1,2,3,4,5,6,7,8]

def closure(method, param):
  def t(x):
    return method(x, param)
  return t

f = closure(pow, 2)
f(10)
f = closure(pow, 3)
f(10)

Ответ 5

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

partial(lambda p, x: pow(x, p), 2)

Ответ 6

Один из способов сделать это:

def testfunc1(xs):
    from functools import partial
    def mypow(x,y): return x ** y
    return list(map(partial(mypow,y=2),xs))

но это включает переопределение функции pow.

если использование частичного не было "необходимо", тогда простая лямбда сделала бы трюк

def testfunc2(xs):
    return list(map(lambda x: pow(x,2), xs))

И конкретным способом сопоставления pow of 2 будет

def testfunc5(xs):
    from operator import mul
    return list(map(mul,xs,xs))

но ни один из них не полностью решает проблему непосредственно частичного применения в отношении аргументов ключевого слова

Ответ 7

Как уже говорилось, ограничение functools.partial, если функция, которую вы хотите использовать partial, не принимает аргументы ключевого слова.

Если вы не возражаете использовать внешнюю библиотеку 1 вы можете использовать iteration_utilities.partial, который имеет частичный, который поддерживает заполнители:

>>> from iteration_utilities import partial
>>> square = partial(pow, partial._, 2)  # the partial._ attribute represents a placeholder
>>> list(map(square, xs))
[1, 4, 9, 16, 25, 36, 49, 64]

1 Отказ от ответственности: я являюсь автором библиотеки iteration_utilities (инструкции по установке могут быть найдены в документации в случае, если вам интересно).