Можно ли частично применить второй аргумент функции, которая не принимает аргументы с ключевым словом?
Возьмем, к примеру, питон, встроенный в функцию 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
Я знаю, что понимание списков будет проще.
Ответы
Ответ 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
(инструкции по установке могут быть найдены в документации в случае, если вам интересно).