2012-03-02 10 views
46

Ho una funzione Python che accetta un elenco come parametro. Se ho impostato il valore di default del parametro a una lista vuota come questa:Best practice per l'impostazione del valore predefinito di un parametro che dovrebbe essere un elenco in Python?

def func(items=[]): 
    print items 

pylint mi diceva "valore predefinito pericolose [] come argomento". Quindi mi stavo chiedendo qual è la migliore pratica qui?

+3

questo è qualcosa che ogni principiante di pitone morde il dito del piede una o due volte, è davvero bello che la stampante abbia impedito di scrivere un bug orribile! – wim

risposta

64

Usa None come valore di default:

def func(items=None): 
    if items is None: 
     items = [] 
    print items 

Il problema con un argomento predefinito mutabile è che sarà condiviso tra tutte le chiamate di funzione - vedere la "importante avvertimento" nel relevant section of the Python tutorial.

+2

Ah wow, sono sorpreso che la gente lo sappia. =) Ho odiato così tanto questa funzionalità di python che ho scritto la mia libreria decorator per consentire la sintassi 'func (items = new ([]))'. – ninjagecko

+0

Grazie! Sembra che Python pensi che None sia un tipo completamente diverso dall'elenco, quindi mi chiedevo se fosse generalmente accettabile in Python impostare il valore predefinito di un parametro su un tipo diverso (come None) (So che Python è un tipo-less lingua, ma vengo da C++ ... lol)? –

+0

È molto diverso da un linguaggio fortemente tipizzato come il C++. Basti pensare alle variabili come nomi che fanno riferimento agli oggetti, e tutto viene passato per riferimento. – wim

2

Per l'oggetto mutabile come parametro predefinito nelle dichiarazioni di funzione e metodo, il problema è che la valutazione e la creazione avvengono esattamente nello stesso momento. Il parser python legge la funzione-head e la valuta nello stesso momento.

La maggior parte dei principianti non riesce a creare un nuovo oggetto ad ogni chiamata, ma non è corretto! Un oggetto (nel tuo esempio un elenco) viene creato al momento della DICHIARAZIONE e non su richiesta quando si chiama il metodo.

Per gli oggetti non modificabili non è un problema, perché anche se tutte le chiamate condividono lo stesso oggetto, è imutibile e quindi le sue proprietà rimangono le stesse.

Come convenzione si utilizza l'oggetto None per i valori predefiniti per indicare l'utilizzo di un'inizializzazione predefinita, che ora può avvenire nel corpo della funzione, che viene naturalmente valutato al momento della chiamata.

0

Inoltre, e anche per capire meglio cosa pitone è, qui il mio piccolo frammento tema:

from functools import wraps 
def defaultFactories(func): 
    'wraps function to use factories instead of values for defaults in call' 
    defaults = func.func_defaults 
    @wraps(func) 
    def wrapped(*args,**kwargs): 
     func.func_defaults = tuple(default() for default in defaults) 
     return func(*args,**kwargs) 
    return wrapped 

def f1(n,b = []): 
    b.append(n) 
    if n == 1: return b 
    else: return f1(n-1) + b 

@defaultFactories 
def f2(n,b = list): 
    b.append(n) 
    if n == 1: return b 
    else: return f2(n-1) + b 

>>> f1(6) 
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1] 
>>> f2(6) 
[1, 2, 3, 4, 5, 6] 
4

Ho appena incontrato questo per la prima volta, e il mio pensiero immediato è "beh, io non voglio per mutare comunque la lista, quindi quello che voglio veramente è di default in una lista immutabile, quindi Python mi darà un errore se lo metto per sbaglio. " Una lista immutabile è solo una tupla. Quindi:

 
    def func(items=()): 
     print items 

Certo, se si passa a qualcosa che non vuole veramente un elenco (ad esempio isinstance (articoli, lista)), allora questo otterrà nei guai. Ma questo è un odore di codice comunque.

+1

E se all'interno della funzione è necessario fare una copia, usare 'my_copy = lista (elementi)'. Hai trovato una soluzione semplice e molto intelligente per un problema comune. –

Problemi correlati