2012-06-23 12 views
21

Tomemos por ejemplo el python incorporado en la función pow().¿Se puede aplicar parcialmente el segundo argumento de una función que no toma argumentos de palabra clave?

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 el 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é listas por comprensión serían más fáciles.

+0

otro uso de parciales a partir del argumento 2-nd es parcial para los métodos que omiten el autoargumento –

Respuesta

26

Sin

Según the documentation, partial no puede hacer esto (el subrayado es mío):

parcial.args

Los más a la izquierda argumentos posicionales que se antepone a los argumentos posicionales


siempre se puede simplemente "fijar" pow tener argumentos de palabras clave:

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

ahora, ¿por qué no leí la documentación? P.S hermoso formato de esta respuesta. +1 – beoliver

+0

si en python 4.X pow() es "fijo", todos sabremos de dónde sacaron su idea :) – beoliver

+0

@beoliver: en realidad, a partir de [PEP-457] (https: //www.python .org/dev/peps/pep-0457 /), los argumentos solo posicionales se formalizan en lo que parece ser una característica, por lo que nunca esperaría una solución para 'pow' – Eric

2

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 la redefinición de la función pow.

si el uso de parcial no fue 'necesario', entonces un lambda sencilla sería hacer el truco

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

Y una manera específica para mapear el prisionero de guerra de 2 sería

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

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

+0

Esto debe agregarse a la pregunta, no publicada como respuesta. – user545424

+0

ayer me aconsejaron agregar mis intentos como una respuesta en lugar de la pregunta. ¡Es una situación sin victoria! – beoliver

+1

Creo que está bien. Debe expresarlo más como "Aquí hay una forma de hacerlo" en lugar de agregarlo a la pregunta. Hasta cierto punto, imagina que eres otra persona que responde. – Eric

6

Puede 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 esto es lo que @wraps debería usarse, pero parecía correcto.

+0

Major +1. Estuve tentado de seleccionar esta respuesta, pero supongo que estaba pensando más en el 'functools.partial' incorporado. Pero esto definitivamente se está salvando. Me gusta :) – beoliver

5

podría utilizar 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) 
+1

+1 agradable y simple, supongo que podría construir una pequeña biblioteca de varios cierres. :) ... ahora cuál estaba buscando nuevamente. – beoliver

11

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

Actualización:

También se le ocurrió una solución más divertido que útiles. Es un azúcar sintáctico hermoso, aprovechando el hecho de que el 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 ya no puedes pasar Ellipsis 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 de la pregunta inicial sería con esta versión de parcial list(map(partial(pow, ..., 2),xs))

+1

agradable, por alguna razón nunca uso repetir. y como estoy en 3.X, es solo 'lista (mapa (pow, [1, 2, 3], itertools.repeat (2)))' – beoliver

+0

bien, no sabía que cambiaron 'map' en Python3 – kosii

+0

izip ahora también está zip EDIT ... ¡¡¡Acabo de ver tu actualización !!! ;) – beoliver

5

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

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

Como Ya dije que eso es una limitación de functools.partial si la función que desea partial no acepta argumentos de palabra clave.

Si no les importa usar una biblioteca externa podría utilizar iteration_utilities.partial que tiene una parcial que soporta 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] 

responsabilidad: yo soy el autor de la biblioteca iteration_utilities (las instrucciones de instalación se pueden encontrar en in the documentation en caso de que le interese).

1

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

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

De manera más general:

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 

Uno menos del lado de lambda es que algunas bibliotecas no son capaces de serializar eso.

+0

¿Qué quiere decir con algunas bibliotecas? Creo que ninguna biblioteca puede serializarlos. – MSeifert

+0

A menudo uso ruamel.yaml y serializa lambdas bien. – danijar

+0

Bien, estaba pensando en los módulos incorporados. Gracias por la aclaración :) – MSeifert

Cuestiones relacionadas