¿Se puede aplicar parcialmente el segundo argumento de una función que no toma argumentos de palabras clave?

Tomemos, por ejemplo, la función python integrada en 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] 

¿Pero cómo elevaría las xs a la potencia de 2?

para obtener [1, 4, 9, 16, 25, 49, 64]

 list(map(partial(pow,y=2),xs)) TypeError: pow() takes no keyword arguments 

Sé que la comprensión de la lista sería más fácil.

    No

    Según la documentación , partial no puede hacer esto (énfasis mío):

    arcos parciales

    Los argumentos posicionales más a la izquierda que se añadirán a los argumentos posicionales.


    Siempre se puede simplemente “arreglar” el poder tener argumentos de palabras clave:

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

    Creo que solo usaría este sencillo de una sola línea:

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

    Actualizar:

    También se me ocurrió una solución más divertida que útil. Es un hermoso azúcar sintáctico, aprovechando el hecho de que ... literal significa Ellipsis en Python3. Es una versión modificada de partial , que permite omitir algunos argumentos posicionales entre los más a la izquierda y los más a la derecha. El único inconveniente es que no se puede pasar más elipsis como argumento.

     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 

    Entonces, la solución para la pregunta original sería con esta versión de list(map(partial(pow, ..., 2),xs)) parcial list(map(partial(pow, ..., 2),xs))

    ¿Por qué no simplemente crear una función lambda rápida que reordena los argumentos y parcial que

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

    Podrías crear una función auxiliar para esto:

     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) 

    Descargo de responsabilidad: no he probado esto con argumentos de palabras clave por lo que podría explotar debido a ellos de alguna manera. Además, no estoy seguro de si para eso se debe usar @wraps pero parece correcto.

    podrías usar un cierre

     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) 

    Una forma de hacerlo sería:

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

    pero esto implica redefinir la función pow.

    Si el uso de parcial no fuera ‘necesario’, entonces un simple lambda haría el truco.

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

    Y una forma específica de mapear el poder de 2 sería

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

    pero ninguno de estos aborda completamente el problema directamente de la aplicación parcial en relación con los argumentos de palabras clave

    Puedes hacer esto con lambda , que es más flexible que functools.partial() :

     pow_two = lambda base: pow(base, 2) print(pow_two(3)) # 9 

    Más generalmente:

     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 

    Un inconveniente de la lambda es que algunas bibliotecas no pueden serializarlo.

    La muy versátil función incluye una función rpartial que aborda exactamente este problema.

     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] 

    Es solo un lambda bajo el capó:

     def rpartial(func, *args): """Partially applies last arguments.""" return lambda *a: func(*(a + args)) 

    Como ya se dijo, eso es una limitación de functools.partial si la función que desea partial no acepta argumentos de palabras clave.

    Si no te importa usar una biblioteca externa 1, puedes usar iteration_utilities.partial que tiene un parcial que admite marcadores de posición:

     >>> 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 Descargo de responsabilidad: soy el autor de la biblioteca iteration_utilities (las instrucciones de instalación se pueden encontrar en la documentación en caso de que esté interesado).

    Si no puede usar las funciones lambda, también puede escribir una función de envoltura simple que reordene los argumentos.

     def _pow(y, x): return pow(x, y) 

    y luego llamar

     list(map(partial(_pow,2),xs)) >>> [1, 4, 9, 16, 25, 36, 49, 64]