2012-06-23 14 views
21

Prendiamo ad esempio il python integrato nella funzione pow().Si può applicare parzialmente il secondo argomento di una funzione che non accetta argomenti di parole chiave?

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] 

ma come potrei aumentare il xs alla potenza di 2?

per ottenere [1, 4, 9, 16, 25, 49, 64]

list(map(partial(pow,y=2),xs)) 

TypeError: pow() takes no keyword arguments 

so list comprehension sarebbe più facile.

+0

un altro utilizzo di parziali partendo da 2-nd argomento è parziale per metodo (i) omettendo l'argomento auto –

risposta

26

No

Secondo the documentation, partialnon può fare questo (enfasi mia):

parziale.args

I più a sinistra argomenti posizionali che verrà anteposto al argomenti posizionali


Si può sempre e solo "sistemare" pow avere args Parola chiave:

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

ora perché non ho letto la documentazione. P.S bella formattazione di questa risposta. +1 – beoliver

+0

se in python 4.X pow() è "risolto", sapremo tutti da dove hanno avuto la loro idea :) – beoliver

+0

@beoliver: In realtà, a partire da [PEP-457] (https: //www.python .org/dev/peps/pep-0457 /), gli argomenti solo posizionali sono formalizzati in quella che sembra essere una caratteristica, quindi non mi aspetterei mai una correzione per 'pow' – Eric

2

Un modo per farlo sarebbe:

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

ma questo comporta la ridefinizione della funzione pow.

se l'uso di parziale non è stata 'necessaria' quindi un semplice lambda farebbe il trucco

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

E un modo specifico per mappare la pow di 2 sarebbe

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

ma nessuno di questi completamente affrontare il problema direttamente dell'applicativo parziale in relazione agli argomenti delle parole chiave

+0

Questo dovrebbe essere aggiunto alla domanda, non pubblicato come risposta. – user545424

+0

ieri mi è stato consigliato di aggiungere i miei tentativi come risposta in contrasto con la domanda. È una situazione senza vittorie! – beoliver

+1

Penso che qui sia OK. Dovresti esprimerlo più come "Ecco un modo per farlo" piuttosto che come aggiunta alla domanda. In una certa misura, fingi di essere qualcun altro a rispondere. – Eric

6

È possibile creare una funzione di supporto per questo:

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) 

Disclaimer: Non ho provato questo con gli argomenti delle parole chiave in modo che potrebbe esplodere a causa di loro in qualche modo. Inoltre non sono sicuro se questo è quello che dovrebbe essere usato per @wraps ma sembrava giusto -ish.

+0

Maggiore +1. Sono stato tentato di selezionare questa risposta, ma suppongo che stavo pensando di più al "functools.partial" integrato. Ma questo è sicuramente salvato. Mi piace :) – beoliver

5

si potrebbe usare una chiusura

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 bello e semplice, suppongo che potrei costruire una piccola biblioteca di varie chiusure. :) ... ora quale cercavo di nuovo. – beoliver

11

Penso che vorrei usare questa semplice one-liner:

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

Aggiornamento:

Ho anche si avvicinò con una soluzione di più divertente di utile. È un bellissimo zucchero sintattico, approfittando del fatto che il letterale ... significa Ellipsis in Python3. È una versione modificata di partial, che consente di omettere alcuni argomenti posizionali tra quelli più a sinistra e quelli più a destra. L'unico inconveniente è che non puoi più passare Ellipsis come argomento.

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 

Quindi la soluzione per la domanda iniziale sarebbe con questa versione di parziale list(map(partial(pow, ..., 2),xs))

+1

bello, per qualche ragione non uso mai ripetere. e siccome sono su 3.X, è solo 'lista (map (pow, [1, 2, 3], itertools.repeat (2)))' – beoliver

+0

nice, non sapevo che hanno cambiato 'map' in Python3 – kosii

+0

izip ora è anche zip EDIT ... Ho appena visto il tuo aggiornamento !!! ;) – beoliver

5

Perché non solo creare una funzione lambda rapido che riordina i args e parziali che

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

Come già detto che è una limitazione di functools.partial se la funzione che si desidera partial non accetta argomenti di parole chiave.

Se non ti dispiace utilizzando una libreria esterna si potrebbe usare iteration_utilities.partial che ha un parziale che supporta segnaposto:

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

Disclaimer: Sono l'autore di la libreria iteration_utilities (è possibile trovare le istruzioni di installazione in the documentation nel caso siate interessati).

1

È possibile farlo con lambda, che è più flessibile rispetto functools.partial():

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

Più in generale:

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 rovescio della medaglia di lambda è che alcune librerie non sono in grado di serializzare esso.

+0

Cosa intendi con alcune librerie? Penso che nessuna libreria sia in grado di serializzarli. – MSeifert

+0

Sto spesso usando ruamel.yaml e serializza bene lambdas. – danijar

+0

Ok, stavo pensando ai moduli integrati. Grazie per il chiarimento :) – MSeifert

Problemi correlati