Можно ли частично применить второй аргумент функции, которая не принимает аргументов с ключевыми словами?
Возьмите для примера встроенный питон 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]
но как бы я поднял х до степени 2?
получить [1, 4, 9, 16, 25, 49, 64]
list(map(partial(pow,y=2),xs))
TypeError: pow() takes no keyword arguments
Я знаю, что понимание списка будет проще.
13 ответов
нет
Согласно документации, partial
не могу сделать это (выделю мое):
partial.args
Самые левые позиционные аргументы, которые будут добавлены к позиционным аргументам
Вы всегда можете просто "исправить" pow
иметь ключевые слова args:
_pow = pow
pow = lambda x, y: _pow(x, y)
Почему бы просто не создать быструю лямбда-функцию, которая переупорядочивает аргументы и частично
partial(lambda p, x: pow(x, p), 2)
Я думаю, что я просто использовал бы эту простую однострочную:
import itertools
print list(itertools.imap(pow, [1, 2, 3], itertools.repeat(2)))
Обновить:
Я также придумал более смешное, чем полезное решение. Это красивый синтаксический сахар, извлекающий выгоду из того факта, что ...
буквальные средства 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))
Вы можете создать вспомогательную функцию для этого:
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
следует использовать для, но это казалось правильным - иш.
Вы могли бы использовать закрытие
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)
Вы можете сделать это с lambda
, который является более гибким, чем functools.partial()
:
pow_two = lambda base: pow(base, 2)
print(pow_two(3)) # 9
В более общем смысле:
def bind_skip_first(func, *args, **kwargs):
return lambda first: func(first, *args, **kwargs)
pow_two = bind_skip_first(pow, 2)
print(pow_two(3)) # 9
Недостатком лямбды является то, что некоторые библиотеки не могут ее сериализовать.
Один из способов сделать это будет:
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 2 будет
def testfunc5(xs):
from operator import mul
return list(map(mul,xs,xs))
но ни один из них полностью не решает проблему частичного применения в отношении аргументов ключевых слов
Несмотря на то, что на этот вопрос уже был дан ответ, вы можете получить желаемый результат с рецептом, взятым из itertools.repeat
:
from itertools import repeat
xs = list(range(1, 9)) # [1, 2, 3, 4, 5, 6, 7, 8]
xs_pow_2 = list(map(pow, xs, repeat(2))) # [1, 4, 9, 16, 25, 36, 49, 64]
Надеюсь, это кому-то поможет.
Да, вы можете это сделать, если функция принимает аргументы ключевого слова. Просто нужно знать имя.
В случаеpow()
(при условии, что вы используете Python 3.8 или новее) вам нужноexp
вместо Ю.
Стараться сделать:
xs = [1,2,3,4,5,6,7,8]
print(list(map(partial(pow,exp=2),xs)))
Очень универсальная забава включает в себя rpartial
функция, которая точно решает эту проблему.
xs = [1,2,3,4,5,6,7,8]
from funcy import rpartial
list(map(rpartial(pow, 2), xs))
# [1, 4, 9, 16, 25, 36, 49, 64]
Это просто лямбда под капотом
def rpartial(func, *args):
"""Partially applies last arguments."""
return lambda *a: func(*(a + args))
Как уже говорилось, это ограничение 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
библиотека (инструкции по установке можно найти в документации на случай, если вы заинтересованы).
да
если вы создали свой частичный класс
class MyPartial:
def __init__(self, func, *args):
self._func = func
self._args = args
def __call__(self, *args):
return self._func(*args, *self._args) # swap ordering
xs = [1,2,3,4,5,6,7,8]
list(map(MyPartial(pow,2),xs))
>>> [1, 4, 9, 16, 25, 36, 49, 64]
Если вы не можете использовать лямбда-функции, вы также можете написать простую функцию-обертку, которая переупорядочивает аргументы.
def _pow(y, x):
return pow(x, y)
а затем позвоните
list(map(partial(_pow,2),xs))
>>> [1, 4, 9, 16, 25, 36, 49, 64]