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

Как заполнить определенные позиционные аргументы частичным в 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 можно использовать.

4b9b3361

Ответ 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)