Как заполнить определенные позиционные аргументы частичным в python?
В принципе, я бы хотел:
>>> from functools import partial
>>> partial(str.startswith, prefix='a')('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: startswith() takes no keyword arguments
Но более общий вопрос: как заполнить конкретные позиционные аргументы с помощью partial
.
P.S. Я понимаю, что вместо lambda
можно использовать.
Ответы
Ответ 1
Это невозможно. Вы должны сделать функцию обертки.
Якобы, вы бы использовали аргументы ключевых слов, как вы пытались сделать - для чего они нужны, не так ли? К сожалению, как вы обнаружили, стандартные функции библиотеки python не принимают именованные параметры. Таким образом, невозможно реализовать текущую реализацию partial
, не создавая другой функции для запуска помех.
В соответствии с принятием PEP 309, для принятия было принято решение "привязка конструктора типа partial() leftmostсильные > позиционные аргументы и любые ключевые слова". Кроме того, в нем говорится:
Обратите внимание, что когда объект вызывается, как если бы он был функцией, позиционные аргументы прилагаются к тем, которые были предоставлены конструктору, а аргументы ключевого слова переопределяют и дополняют те, которые предоставляются конструктору.
Позиционные аргументы, аргументы ключевых слов или оба могут быть предоставлены при создании объекта и при его вызове.
Поскольку дополнительные позиционные аргументы добавляются, не было бы способа предоставить какой-либо предшествующий позиционный аргумент (этой реализацией).
Однако он продолжается:
Частично применяя аргументы из right или вставляя аргументы в произвольные позиции, создают свои собственные проблемы, но, ожидая обнаружения хорошей реализации и не путающей семантики, я не Думаю, это должно быть исключено.
Таким образом, он, по-видимому, может быть на столе, но, как он есть, он не реализован именно так.
Для раскрытия, акцент в приведенных выше цитатах был моим собственным.
Ответ 2
Если вам это действительно нужно, вы можете использовать rpartial из funcy
сторонней библиотеки.
Его код здесь:
def rpartial(func, *args):
return lambda *a: func(*(a + args))
Итак, ваш случай можно обработать следующим образом:
>>> startswith_a = rpartial(str.startswith, 'a')
>>> startswith_a('abc')
True
>>> startswith_a('def')
False
Ответ 3
Используйте этот код:
# See: functoolspartial for binding...
class Function(object):
def __init__(self, fn):
self.fn = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
def __add__(self, other):
def subfn(*args, **kwargs):
return self(*args, **kwargs) + other(*args, **kwargs)
return subfn
class arg(object):
"""Tagging class"""
def __init__(self, index):
self.index = index
class bind(Function):
"""Reorder positional arguments.
Eg: g = f('yp', _1, 17, _0, dp=23)
Then g('a', 'b', another=55) --> f('yp', 'b', 17, 'a', dp=23, another=55)
"""
def __init__(self, fn, *pargs, **pkwargs):
# Maximum index referred to by the user.
# Inputs to f above this index will be passed through
self.fn = fn
self.pargs = pargs
self.pkwargs = pkwargs
self.max_gindex = max(
(x.index if isinstance(x, arg) else -1 for x in pargs),
default=-1)
def __call__(self, *gargs, **gkwargs):
fargs = \
[gargs[x.index] if isinstance(x, arg) else x for x in self.pargs] + \
list(gargs[self.max_gindex+1:])
fkwargs = dict(self.pkwargs)
fkwargs.update(gkwargs) # Overwrite keys
return self.fn(*fargs, *fkwargs)
Затем попробуйте:
def myfn(a,b,c):
print(a,b,c)
bind(myfn, arg(1), 17, arg(0))(19,14)