una propias procedimientos por pasar parametros parametro omision metodos lista funciones explicacion como argumentos python arguments partial-application

propias - parametros por omision python



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

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)


¿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)


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).


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

Así que 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))


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))


Podrías crear una función de ayuda 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 esto es para lo que se debe usar @wraps pero parece correcto.


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.


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]


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


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)