2010-11-17 7 views
6

La mia funzione prevede un elenco o una tupla come parametro. Essa in realtà non importa quale è, tutto ciò che fa è passare a un'altra funzione che accetta una lista o tupla:Parametro funzione Python: tupla/lista

def func(arg): # arg is tuple or list 
    another_func(x) 
    # do other stuff here 

ora ho bisogno di modificare la funzione un po ', per elaborare un ulteriore elemento:

def func(arg): #arg is tuple or list 
    another_func(x + ['a']) 
    # etc 

Sfortunatamente questo non funzionerà: se arg è tupla, devo dire x + ('a',).

Ovviamente, posso farlo funzionare forzando arg per elencare. Ma non è pulito.

C'è un modo migliore per farlo? Non posso costringere i chiamanti a passare sempre una tupla, naturalmente, dato che si limita a lavorare su di loro.

+0

Se l'altra funzione accetta o meno, vorrei inviare una tupla in quanto sarà più veloce con cui lavorare. Quindi cosa non va bene per 'tuple (x) + ('a',)'? – aaronasterling

+0

Le tuple sono più veloci nell'implementazione? – max

+2

costruire una tupla da stringhe e valori letterali numerici è più veloce della costruzione di un elenco. Ancora più importante, 'tuple (x)' restituisce semplicemente 'x' se è già una tupla mentre' list (x) 'copia' x' anche se è già una lista. Quindi, usando una tupla, si taglia la maggior parte del lavoro per metà dei casi di input. – aaronasterling

risposta

7

Se another_func vuole solo un iterabile si può passare itertools.chain(x,'a') ad esso.

+0

+1 Mi piace ma è veloce? – max

+0

@max Nessuna copia è stata creata, quindi è molto veloce. È probabilmente la soluzione Python più efficiente. –

3

come circa:

l = ['a'] 
l.extend(x) 

Edit: Rileggendo domanda, penso che questo sia più ciò che vuoi (l'uso di arg e x era un po 'di confusione):

tuple(arg) + ('a',) 

Come altri hanno già detto, questo probabilmente non è il modo più efficiente, ma è molto chiaro. Se le tue tuple/elenchi sono piccole, potrei usare questa soluzione con soluzioni meno chiare in quanto il risultato della performance sarà trascurabile. Se le tue liste sono grandi, usa le soluzioni più efficienti.

+0

'extend' restituisce' None', poiché muta la lista. – delnan

+0

+1, semplice e pulito – slezica

+0

@delnan: cosa intendi? – max

4

Cosa ne pensi di cambiare l'altra funzione per accettare invece un elenco di parametri?

def func(arg): # arg is tuple or list 
    another_func('a', *x) 
+1

E quando another_func ha bisogno di diversi argomenti di lista che sono interessati dallo stesso problema , o se ha già un parametro (non correlato) * x? – max

+0

+0: buono ma non sicuro se è possibile modificare altre funzioni – Claudiu

+0

@max: la risposta è la stessa. Correggi l'altra funzione. –

2
def f(*args): 
    print args 

def a(*args): 
    k = list(args) 
    k.append('a') 
    f(*k) 

a(1, 2, 3) 

uscita:

(1, 2, 3, 'a') 
+0

hmm 'f (* (tuple (args) + ('a',))' è migliore – Claudiu

1

Il mio suggerimento:

def foo(t): 
    bar(list(t) + [other]) 

Questo non è molto efficiente, però, si sarebbe meglio passare le cose intorno mutabili se si sta andando ad essere , bene, mutandoli.

+0

Puoi chiarire per favore? Perché non è efficiente? – max

+0

@max Non è così efficiente perché copia un'intera lista solo per aggiungere un singolo articolo –

0

La funzione accetta qualsiasi iterabile. Quindi utilizzare itertools.chain per aggiungere qualsiasi sequenza si desidera al iterable.

from itertools import chain 

def func(iterable): 
    another_func(chain(iterable, ('a',))) 
1

È possibile utilizzare il tipo di iterabile passata alla prima funzione di costruire quello che si passa alla seconda:

from itertools import chain 

def func(iterable): 
    it = iter(iterable) 
    another_func(type(iterable)(chain(it, ('a',)))) 

def another_func(arg): 
    print arg 

func((1,2)) 
# (1, 2, 'a') 
func([1,2]) 
# [1, 2, 'a'] 
2

Se un iterabile è sufficiente è possibile utilizzare itertools.chain, ma essere consapevoli del fatto che se la funzione A (la prima chiamata), itera su iterabile dopo aver chiamato B, è possibile che si verifichino problemi dal momento che i iterabili non possono essere riavvolti.In questo caso si dovrebbe optare per una sequenza o utilizzare iterable.tee per fare una copia del iterabile:

import itertools as it 

def A(iterable): 
    iterable, backup = it.tee(iterable) 
    res = B(it.chain(iterable, 'a')) 
    #do something with res 
    for elem in backup: 
     #do something with elem 

def B(iterable): 
    for elem in iterable: 
     #do something with elem 

Anche se itertools.tee non è poi così efficiente se B consuma tutto o più del iterabile, a quel punto è più semplice convertire lo iterable in uno tuple o uno list.

+0

+1 per il chiarimento sul riavvolgimento degli iteratori Nel mio caso, è abbastanza iterabile, suppongo che per il caso in sequenza mi limiterò a forzare a tupla come suggerito da @aaronasterling – max

0

Direi che la risposta di Santiago Lezica di fare

def foo(t): 
    bar(list(t) + [other]) 

è il migliore perché è il più semplice. (non è necessario importare roba itertools e utilizzare chiamate a catena molto meno leggibili). Ma usalo solo se ti aspetti che sia piccolo. Se t può essere grande, dovresti usare una delle altre soluzioni.