2010-06-03 7 views
42

In Python si può avere una definizione di funzione:forzata denominazione dei parametri in Python

def info(object, spacing=10, collapse=1) 

che potrebbe essere chiamata in uno dei seguenti modi:

info(odbchelper)      
info(odbchelper, 12)     
info(odbchelper, collapse=0)   
info(spacing=15, object=odbchelper) 

grazie a Python che permette di un punto qualsiasi argomenti di ordine, purché siano nominati.

Il problema che stiamo riscontrando è la crescita di alcune delle nostre funzioni più grandi, le persone potrebbero aggiungere parametri tra spacing e collapse, il che significa che i valori errati potrebbero andare a parametri non nominati. Inoltre, a volte non è sempre chiaro in che cosa deve andare. Stiamo cercando un modo per costringere le persone a nominare determinati parametri - non solo uno standard di codifica, ma idealmente un plug-in per flag o pydev?

in modo che nei 4 esempi precedenti, solo l'ultimo passasse il controllo in quanto tutti i parametri sono denominati.

Le probabilità sono che lo accendiamo solo per determinate funzioni, ma qualsiasi suggerimento su come implementarlo - o se è persino possibile sarebbe apprezzato.

risposta

94

In Python 3 - Sì, è possibile specificare * nell'elenco degli argomenti.

I parametri dopo "*" o "* identificatore" sono parametri solo parole chiave e possono essere passati solo argomenti parola chiave utilizzati.

codice di esempio (from python ref):

>>> def foo(pos, *, forcenamed): 
... print(pos, forcenamed) 
... 
>>> foo(pos=10, forcenamed=20) 
10 20 
>>> foo(10, forcenamed=20) 
10 20 
>>> foo(10, 20) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: foo() takes exactly 1 positional argument (2 given) 
+4

Per le persone che cercano la fonte della sua citazione, è nei documenti Python 3: https://docs.python.org/3.5/reference/compound_stmts.html#function-definitions – phoenix

+0

Con ** kwargs: 'def foo (pos, *, forcenamed, ** kwargs) '. – Jerther

0

è possibile utilizzare il ** dell'operatore:

def info(**kwargs): 

questo modo le persone sono costrette ad utilizzare parametri denominati.

+0

E non hai idea di come chiamare il tuo metodo senza leggere il tuo codice, aumentare il carico cognitivo sul tuo consumatore :( – Dagrooms

1

È possibile dichiarare le proprie funzioni solo come ricevute da **args. Che sarebbe mandato argomenti chiave, ma che avresti avuto qualche lavoro extra per assicurarsi che i nomi solo validi sono passati in

def foo(**args): 
    print args 

foo(1,2) # Raises TypeError: foo() takes exactly 0 arguments (2 given) 
foo(hello = 1, goodbye = 2) # Works fine. 
+0

Non solo devi aggiungere controlli di parole chiave, ma pensa a un consumatore che sa di dover chiamare un metodo con la firma 'foo (** kwargs)'. Cosa devo passare in questo? 'foo (killme = True, when =" rightnowplease ")' – Dagrooms

+0

Dipende molto da quello che considero 'dict'. –

2

Aggiornamento:.

mi sono reso conto che l'utilizzo di **kwargs non risolverebbe il problema. Se i programmatori cambiano gli argomenti della funzione come vogliono, si potrebbe, ad esempio, modificare la funzione a questo:

def info(foo, **kwargs): 

e il vecchio codice potrebbe rompere di nuovo (perché ora ogni chiamata di funzione deve includere il primo argomento).

Si tratta davvero di ciò che dice Bryan.


(...) la gente potrebbe essere aggiunta parametri tra spacing e collapse (...)

In generale, quando si cambia funzioni, nuovi argomenti dovrebbero sempre andare fino alla fine. Altrimenti rompe il codice. Dovrebbe essere ovvio.
Se qualcuno cambia la funzione in modo che il codice si interrompa, questa modifica deve essere rifiutata.
(come dice Bryan, è come un contratto)

(...) a volte non è sempre chiaro da ciò che deve andare in.

Osservando la firma della funzione (cioè def info(object, spacing=10, collapse=1)) si dovrebbe vedere subito che ogni argomento che ha non un valore di default, è obbligatoria.
Cosa l'argomento è per, dovrebbe andare in docstring.


Vecchio risposta (mantenuto per completezza):

Questo probabilmente non è una buona soluzione:

È possibile definire le funzioni in questo modo:

def info(**kwargs): 
    ''' Some docstring here describing possible and mandatory arguments. ''' 
    spacing = kwargs.get('spacing', 15) 
    obj = kwargs.get('object', None) 
    if not obj: 
     raise ValueError('object is needed') 

kwargs è un dizionario che contiene argomenti di parole chiave. È possibile verificare se è presente un argomento obbligatorio e, in caso contrario, sollevare un'eccezione.

Lo svantaggio è che potrebbe non essere più così ovvio, quali argomenti sono possibili, ma con una docstring adeguata, dovrebbe andare bene.

+3

Mi è piaciuta la tua vecchia risposta meglio. Metti un commento sul motivo per cui stai accettando solo ** kwargs nella funzione.Dopo tutto, chiunque può cambiare qualcosa nel codice sorgente - hai bisogno di documentazione per descrivere l'intento e lo scopo dietro le tue decisioni – Brandon

0
def cheeseshop(kind, *arguments, **keywords): 

in python, se uso * args Ciò significa che è possibile passare n-numero di argomenti per questo parametro - che sarà venuto una lista di funzioni all'interno di accedere

e se l'uso * * kw che significa i suoi argomenti di parole chiave, che può essere accesso come dict - puoi passare n-numero di kw args, e se vuoi limitare quell'utente deve inserire la sequenza e gli argomenti in ordine quindi non usare * e ** - (il suo modo pirotecnico di fornire soluzioni generiche per le grandi architetture ...)

se si vuole restringere la funzione con i valori di default, allora è possibile controllare al suo interno

def info(object, spacing, collapse) 
    spacing = spacing or 10 
    collapse = collapse or 1 
0

Come dicono altre risposte, cambiando le firme di funzione è una cattiva idea. Aggiungere nuovi parametri alla fine o correggere ogni chiamante se gli argomenti sono inseriti.

Se si desidera continuare, utilizzare la funzione function decorator e inspect.getargspec. Ma potrebbe essere utilizzato o meno così:

@require_named_args 
def info(object, spacing=10, collapse=1): 
    .... 

Attuazione require_named_args è lasciata come esercizio per il lettore.

Non mi preoccuperei. Sarà lento ogni volta che viene chiamata la funzione e otterrai risultati migliori dalla scrittura del codice più attentamente.

-2

Non capisco perché un programmatore aggiungerà un parametro tra altri due in primo luogo.

Se si desidera utilizzare i parametri di funzione con i nomi (ad esempio info(spacing=15, object=odbchelper)), non dovrebbe importare l'ordine in cui sono definiti, quindi è consigliabile inserire i nuovi parametri alla fine.

Se vuoi che l'ordine abbia importanza, non puoi aspettarti che qualcosa funzioni se lo cambi!

+0

Questo non risponde alla domanda Che sia o meno una buona idea è irrilevante - qualcuno potrebbe farlo comunque. –

+0

Come ha detto Graeme, qualcuno lo farà comunque. se si sta scrivendo una libreria per essere utilizzata da altri, forzando (solo python 3) il passaggio di argomenti solo per parole chiave consente una maggiore flessibilità quando si deve rifattorizzare la propria API. – s0undt3ch

14

È possibile forzare le persone a utilizzare gli argomenti delle parole chiave in Python3 definendo una funzione nel modo seguente.

def foo(*, arg0="default0", arg1="default1", arg2="default2"): 
    pass 

Rendendo il primo argomento di un argomento posizionale senza nome si forza tutti coloro che chiama la funzione per utilizzare la parola chiave argomenti che è quello che penso che stavi chiedendo circa. In python2 l'unico modo per farlo è quello di definire una funzione come questa

def foo(**kwargs): 
    pass 

Che ti costringono al chiamante di utilizzare kwargs, ma questo non è un gran che di una soluzione come ci si poi mettere un seleziona per accettare solo l'argomento di cui hai bisogno.

7

vero, la maggior parte dei linguaggi di programmazione make ordine dei parametri parte del contratto funzione di chiamata, ma questo non ha bisogno di essere così. Perché dovrebbe? La mia comprensione della domanda è, quindi, se Python è diverso dagli altri linguaggi di programmazione a questo riguardo. In aggiunta alle altre buone risposte per Python 2, si prega di considerare quanto segue:

__named_only_start = object() 

def info(param1,param2,param3,_p=__named_only_start,spacing=10,collapse=1): 
    if _p is not __named_only_start: 
     raise TypeError("info() takes at most 3 positional arguments") 
    return str(param1+param2+param3) +"-"+ str(spacing) +"-"+ str(collapse) 

L'unico modo un chiamante sarebbe in grado di fornire argomenti spacing e collapse posizionale (senza eccezione) sarebbe:

info(arg1, arg2, arg3, module.__named_only_start, 11, 2) 

La convenzione di non utilizzare elementi privati ​​appartenenti ad altri moduli è già di base in Python. Come con Python stesso, questa convenzione per i parametri sarebbe solo parzialmente applicata.

Altrimenti, chiamate sarebbero devono essere della forma:

info(arg1, arg2, arg3, spacing=11, collapse=2) 

Una chiamata

info(arg1, arg2, arg3, 11, 2) 

assegnerebbe valore 11 al parametro _p e un'eccezione aumentata di prima istruzione della funzione.

Caratteristiche:

  • parametri prima _p=__named_only_start Sono ammessi posizionale (o per nome).
  • I parametri dopo _p=__named_only_start devono essere forniti solo per nome (a meno che la conoscenza dell'oggetto speciale sentinella __named_only_start sia stata ottenuta e utilizzata).

Pro:

  • parametri sono espliciti in numero e senso (il più tardi se si scelgono anche nomi buoni, ovviamente).
  • Se la sentinella viene specificata come primo parametro, tutti gli argomenti devono essere specificati per nome.
  • Quando si chiama la funzione, è possibile passare alla modalità posizione utilizzando l'oggetto sentinella __named_only_start nella posizione corrispondente.
  • È possibile prevedere prestazioni migliori rispetto ad altre alternative.

Contro:

  • Controllo si verifica durante l'esecuzione, non in fase di compilazione.
  • Utilizzo di un parametro aggiuntivo (anche se non argomento) e un controllo aggiuntivo. Piccolo degrado delle prestazioni rispetto alle normali funzioni.
  • La funzionalità è un hack senza supporto diretto da parte della lingua (vedere la nota sotto).
  • Quando si chiama la funzione, è possibile passare alla modalità posizione utilizzando l'oggetto sentinella __named_only_start nella posizione corretta. Sì, questo può anche essere visto come un professionista.

Si prega di tenere presente che questa risposta è valida solo per Python 2. Python 3 implementa il meccanismo simile, ma molto elegante, supportato dalla lingua descritto in altre risposte.

Ho scoperto che quando apro la mente e ci penso, nessuna domanda o altra decisione sembra stupida, stupida o semplicemente sciocca. Al contrario, di solito imparo molto.

+0

_ "Il controllo si verifica durante l'esecuzione, non in fase di compilazione." _ - Penso che sia vero per tutto il controllo degli argomenti delle funzioni. Finché non si esegue effettivamente la riga della chiamata di funzione, non si sa sempre quale funzione viene eseguita. Inoltre, ** + 1 ** - questo è intelligente. – Eric

+0

@Eric: È solo che avrei preferito il controllo statico. Ma hai ragione: non sarebbe stato affatto Python. Sebbene non sia un punto decisivo, anche il costrutto "*" di Python 3 viene controllato dinamicamente. Grazie per il tuo commento. –

+0

Inoltre, se si nomina la variabile del modulo '_named_only_start', diventa impossibile fare riferimento a un modulo esterno, che estrae un pro e un contro. (singoli trattini bassi al centro del modulo sono privati, IIRC) – Eric

2

È possibile farlo in un modo che funziona sia in Python 2 che in Python 3, creando un "falso" primo argomento parola chiave con un valore predefinito che non si verificherà "naturalmente". Tale argomento parola chiave può essere preceduto da uno o più argomenti senza valore:

_dummy = object() 

def info(object, _kw=_dummy, spacing=10, collapse=1): 
    if _kw is not _dummy: 
     raise TypeError("info() takes 1 positional argument but at least 2 were given") 

Questo permetterà:

info(odbchelper)   
info(odbchelper, collapse=0)   
info(spacing=15, object=odbchelper) 

ma non:

info(odbchelper, 12)     

Se si modifica la funzione di:

def info(_kw=_dummy, spacing=10, collapse=1): 

quindi tutti gli argomenti devono avere parole chiave e info(odbchelper) non funzionerà più.

Ciò consentirà di posizionare ulteriori argomenti di parole chiave in qualsiasi posizione dopo lo _kw, senza imporli di inserirli dopo l'ultima voce. Questo ha spesso senso, ad es. raggruppare le cose in modo logico o organizzare le parole chiave in ordine alfabetico può aiutare con la manutenzione e lo sviluppo.

Quindi non è necessario ripristinare l'utilizzo di def(**kwargs) e perdere le informazioni sulla firma nel proprio editor intelligente.Il tuo contratto sociale è quello di fornire determinate informazioni, forzando (alcune di esse) a richiedere parole chiave, l'ordine in cui queste sono presentate, è diventato irrilevante.

Problemi correlati