2009-04-12 8 views
31

Stavo creando una semplice utility da riga di comando e un dizionario come una sorta di istruzione case con parole chiave che collegano alla funzione appropriata. Le funzioni hanno tutte diverse quantità di argomenti richiesti, così attualmente per verificare se l'utente ha inserito la quantità corretta di argomenti necessari per ogni funzione ho inserito la quantità richiesta all'interno dell'istruzione case dictionary nel modulo {Keyword:(FunctionName, AmountofArguments)}.Determinare in modo programmatico la quantità di parametri richiesta da una funzione - Python

Questa configurazione attuale funziona perfettamente bene però mi stavo chiedendo nell'interesse di sé improval se ci fosse un modo per determinare il numero di argomenti a una funzione ei miei tentativi di Google sono tornati fino ad ora nulla di valore, ma vedo come args e kwargs avrebbero potuto sventare un comando del genere a causa della quantità illimitata di argomenti consentiti.

risposta

36

inspect.getargspec():

ottenere i nomi ei valori predefiniti di argomenti di una funzione. Viene restituita una tupla di quattro elementi: (args, varargs, varkw, defaults). args è una lista dei nomi degli argomenti (può contenere liste annidate). varargs e varkw sono i nomi degli argomenti * e ** o None. i valori di default sono una tupla di valori di argomenti predefiniti o None se non ci sono argomenti predefiniti; se questa tupla ha n elementi, essi corrispondono agli ultimi n elementi elencati in args.

+0

Grazie Ho appena scoperto che molto funzione come hai postato quella risposta. Questo è esattamente quello di cui ho bisogno. – tomeedee

+0

Anche perché non ho il rep per modificare la tua risposta hai errore nella tua risposta è getargspec() not getspspect() – tomeedee

+0

Dovresti usare inspect.getfullargspec (func) (http://docs.python.org/3.1/ library/inspect.html # inspect.getfullargspec) per Python 3.x – Casebash

14

quello che vuoi è, in generale, non è possibile, a causa dell'uso di varargs e kwargs, ma inspect.getargspec (Python 2.x) e inspect.getfullargspec (Python 3.x) si avvicinano.

  • Python 2.x:

    >>> import inspect 
    >>> def add(a, b=0): 
    ...  return a + b 
    ... 
    >>> inspect.getargspec(add) 
    (['a', 'b'], None, None, (0,)) 
    >>> len(inspect.getargspec(add)[0]) 
    2 
    
  • Python 3.x:

    >>> import inspect 
    >>> def add(a, b=0): 
    ...  return a + b 
    ... 
    >>> inspect.getfullargspec(add) 
    FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 
    >>> len(inspect.getfullargspec(add).args) 
    2 
    
+3

'inspect.getargspec (function)' è una tupla con nome, quindi invece di 'len (inspect.getargspec (function) [0])' puoi usare il più leggibile 'len (inspect.getargspec (function) .args)' . –

+0

'getargspec' è stato deprecato da Python 3, invece usa' signature' o 'getfullargspec'. – Akshay

+0

@ 1 '': Questo è vero a partire dalla versione 2.6, non era nella versione precedente che usavo allora. – Stephan202

1

rendono ogni comando una classe, derivata da una base astratta che definisce la struttura generale di un comando. Per quanto possibile, la definizione delle proprietà del comando deve essere inserita in variabili di classe con metodi definiti nella classe base che gestiscono tali dati.

Registrare ciascuna di queste sottoclassi con una classe di fabbrica. Questa classe factory ottiene l'elenco di argomenti che decide quale comando eseguire istanziando la sottoclasse di comando appropriata.

Il controllo degli argomenti viene gestito dalle sottoclassi dei comandi stessi, utilizzando metodi generali correttamente definiti dalla classe base comandi.

In questo modo, non è mai necessario codificare ripetutamente la stessa roba e non è necessario emulare l'istruzione switch. Inoltre, facilita l'estensione e l'aggiunta di comandi, poiché puoi semplicemente aggiungere e registrare una nuova classe. Niente altro da cambiare.

+2

+1: tu, signore, sei divertente. Dato che hai scritto questo dopo che la risposta accettata è stata postata, presumo che tu sia intenzionalmente esprimere opinioni molto convincenti sulla programmazione difensiva piuttosto che tentare di descrivere il modo più semplice per contare quanti args la funzione richiede – user1612868

0

Ottima domanda. Ho appena avuto il problema che volevo scrivere una funzione che accetta un argomento di callback. A seconda del numero di argomenti di tale callback, deve essere chiamato diversamente.

Ho iniziato con la risposta di gimel, quindi mi sono espanso per poter gestire i builtin che non reagiscono bene con il modulo inspect (raise TypeError).

Quindi, ecco il codice per verificare se una funzione si aspetta esattamente un argomento:

def func_has_one_arg_only(func, typical_argument=None, ignore_varargs=False): 
    """True if given func expects only one argument 

    Example (testbench): 
    assert not func_has_one_arg_only(dict.__getitem__), 'builtin 2 args' 
    assert func_has_one_arg_only(lambda k: k), 'lambda 1 arg' 
    assert not func_has_one_arg_only(lambda k,x: k), 'lambda 2 args' 
    assert not func_has_one_arg_only(lambda *a: k), 'lambda *a' 
    assert not func_has_one_arg_only(lambda **a: k), 'lambda **a' 
    assert not func_has_one_arg_only(lambda k,**a: k), 'lambda k,**a' 
    assert not func_has_one_arg_only(lambda k,*a: k), 'lambda k,*a' 

    assert func_has_one_arg_only(lambda k: k, ignore_varargs=True), 'lambda 1 arg' 
    assert not func_has_one_arg_only(lambda k,x: k, ignore_varargs=True), 'lambda 2 args' 
    assert not func_has_one_arg_only(lambda *a: k, ignore_varargs=True), 'lambda *a' 
    assert not func_has_one_arg_only(lambda **a: k, ignore_varargs=True), 'lambda **a' 
    assert func_has_one_arg_only(lambda k,**a: k, ignore_varargs=True), 'lambda k,**a' 
    assert func_has_one_arg_only(lambda k,*a: k, ignore_varargs=True), 'lambda k,*a' 
    """ 

    try: 
     import inspect 
     argspec = inspect.getargspec(func) 
    except TypeError:     # built-in c-code (e.g. dict.__getitem__) 
     try: 
      func(typical_argument) 
     except TypeError: 
      return False 
     else: 
      return True 
    else: 
     if not ignore_varargs: 
      if argspec.varargs or argspec.keywords: 
       return False 
     if 1 == len(argspec.args): 
      return True 
     return False 
    raise RuntimeError('This line should not be reached') 

È possibile controllare il comportamento relative al varargs argomenti *args e **kwargs con il parametro ignore_varargs.

Il parametro typical_argument è un kludge: se inspect non riesce a funzionare, ad es. sui builtin sopracitati, quindi proviamo a chiamare la funzione con un argomento e vediamo cosa succede.

Il problema con questo approccio è che ci sono più motivi per raise TypeError: viene utilizzato il numero errato di argomenti o viene utilizzato il tipo di argomenti errato. Consentendo all'utente di fornire un typical_argument sto tentando di aggirare questo problema.

Questo non è bello. Ma può aiutare la gente a fare la stessa domanda e anche a correre sul fatto che inspect non è in grado di ispezionare le implementazioni di funzioni con codice C. Forse la gente ha un suggerimento migliore?

2

Questo è già stato risposto, ma senza il modulo ispezionare è anche possibile utilizzare someMethod.func_code.co_argcount

3

In Python 3, utilizzare someMethod.__code__.co_argcount

(dal someMethod.func_code.co_argcount non funziona più)

+0

a questa risposta è già stata fornita 3 mesi fa. Controllare le altre risposte prima di rispondere (e naturalmente non copiare altre risposte –

+0

Stavo cercando una risposta a questa domanda e nessuno di quelli elencati era soddisfacente per me. Ho trovato questa risposta altrove e l'ho inserita qui nel caso qualcuno venga a cercare questo problema tramite Google. altre risposte, non ho copiato nessuna risposta – Bobort

+0

guarda la risposta che Josep Valls ha fatto il 4 agosto: "Questo ha già avuto risposta ma senza il modulo inspect puoi anche usare' someMethod.func_code.co_argcount'". Abbastanza simile non è vero? forse è per questo che è stato segnalato per l'attenzione del moderatore. –

Problemi correlati