Potete elencare gli argomenti delle parole chiave che una funzione riceve?


104

Ho un dict, di cui ho bisogno per passare chiave / valori come argomenti di parole chiave .. Ad esempio ..

d_args = {'kw1': 'value1', 'kw2': 'value2'}
example(**d_args)

Funziona bene, ma se ci sono valori nel d_args dict che non sono accettati dalla examplefunzione, ovviamente muore. Ad esempio, se la funzione di esempio è definita comedef example(kw2):

Questo è un problema poiché non controllo né la generazione di d_args, né la examplefunzione .. Entrambi provengono da moduli esterni e exampleaccettano solo alcuni degli argomenti-parola chiave dal dict ..

Idealmente lo farei e basta

parsed_kwargs = feedparser.parse(the_url)
valid_kwargs = get_valid_kwargs(parsed_kwargs, valid_for = PyRSS2Gen.RSS2)
PyRSS2Gen.RSS2(**valid_kwargs)

Probabilmente filtrerò semplicemente il dict, da un elenco di argomenti di parole chiave validi, ma mi chiedevo: c'è un modo per elencare programmaticamente gli argomenti delle parole chiave che una funzione specifica accetta?

Risposte:


150

Un po 'più bello che ispezionare direttamente l'oggetto codice ed elaborare le variabili è usare il modulo inspect.

>>> import inspect
>>> def func(a,b,c=42, *args, **kwargs): pass
>>> inspect.getargspec(func)
(['a', 'b', 'c'], 'args', 'kwargs', (42,))

Se vuoi sapere se è richiamabile con un particolare insieme di argomenti, hai bisogno degli argomenti senza un valore predefinito già specificato. Questi possono essere ottenuti da:

def getRequiredArgs(func):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if defaults:
        args = args[:-len(defaults)]
    return args   # *args and **kwargs are not required, so ignore them.

Quindi una funzione per dire cosa ti manca dal tuo particolare dict è:

def missingArgs(func, argdict):
    return set(getRequiredArgs(func)).difference(argdict)

Allo stesso modo, per verificare la presenza di argomenti non validi, utilizzare:

def invalidArgs(func, argdict):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if varkw: return set()  # All accepted
    return set(argdict) - set(args)

Quindi un test completo se è richiamabile è:

def isCallableWithArgs(func, argdict):
    return not missingArgs(func, argdict) and not invalidArgs(func, argdict)

(Questo è buono solo per quanto riguarda l'analisi degli argomenti di Python. Qualsiasi controllo di runtime per valori non validi in kwargs ovviamente non può essere rilevato.)


Bello! Non conoscevo questa funzione!
DzinX

1
Dato che il metodo che utilizza gli oggetti codice è più o meno identico, c'è un vantaggio nell'aver dovuto importare un modulo in più ...?
jmetz

@jmets - decisamente - è praticamente sempre meglio usare un modulo libreria piuttosto che lanciarne uno tuo. Inoltre, gli attributi sull'oggetto codice sono più interni e possono essere modificati (ad esempio, notare che questo è stato spostato nel codice in pyhon3). L'uso del modulo come interfaccia ti rende un po 'più a prova di futuro nel caso in cui alcuni di questi interni cambino. Farà anche cose che potresti non aver pensato di fare, come lanciare un errore di tipo appropriato su funzioni che non puoi ispezionare (es. Funzioni C).
Brian

13
inspect.getargspec(f)è deprecato a partire da Python 3.0; il metodo moderno è inspect.signature(f).
gerrit

Cordiali saluti, se vuoi supportare Cython e Python, questo metodo non funziona su una funzione Cython'd. L' co_varnamesopzione, d'altra parte, funziona in entrambi.
parte del

32

Questo stamperà i nomi di tutti gli argomenti passabili, parole chiave e non parole chiave:

def func(one, two="value"):
    y = one, two
    return y
print func.func_code.co_varnames[:func.func_code.co_argcount]

Questo perché i primi co_varnamessono sempre i parametri (i prossimi sono le variabili locali, come ynell'esempio sopra).

Quindi ora potresti avere una funzione:

def getValidArgs(func, argsDict):
    '''Return dictionary without invalid function arguments.'''
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validArgs)

Che poi potresti usare in questo modo:

>>> func(**getValidArgs(func, args))

EDIT : una piccola aggiunta: se hai davvero bisogno solo degli argomenti delle parole chiave di una funzione, puoi usare l' func_defaultsattributo per estrarli:

def getValidKwargs(func, argsDict):
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    kwargsLen = len(func.func_defaults) # number of keyword arguments
    validKwargs = validArgs[-kwargsLen:] # because kwargs are last
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validKwargs)

Ora puoi chiamare la tua funzione con argomenti noti, ma kwarg estratti, ad esempio:

func(param1, param2, **getValidKwargs(func, kwargsDict))

Ciò presuppone che funcnon utilizzi alcuno *argso **kwargsmagia nella sua firma.


cosa succede se voglio stampare solo gli argomenti "parole chiave" "chiavi"?
Jia

7

In Python 3.0:

>>> import inspect
>>> import fileinput
>>> print(inspect.getfullargspec(fileinput.input))
FullArgSpec(args=['files', 'inplace', 'backup', 'bufsize', 'mode', 'openhook'],
varargs=None, varkw=None, defaults=(None, 0, '', 0, 'r', None), kwonlyargs=[], 
kwdefaults=None, annotations={})

7

Per una soluzione Python 3, puoi utilizzare inspect.signaturee filtrare in base al tipo di parametri che desideri conoscere.

Prendendo una funzione di esempio con parametri posizionali o parola chiave, solo parola chiave, var posizionale e parola chiave var:

def spam(a, b=1, *args, c=2, **kwargs):
    print(a, b, args, c, kwargs)

Puoi creare un oggetto firma per esso:

from inspect import signature
sig =  signature(spam)

e poi filtra con una lista di comprensione per scoprire i dettagli di cui hai bisogno:

>>> # positional or keyword
>>> [p.name for p in sig.parameters.values() if p.kind == p.POSITIONAL_OR_KEYWORD]
['a', 'b']
>>> # keyword only
>>> [p.name for p in sig.parameters.values() if p.kind == p.KEYWORD_ONLY]
['c']

e, allo stesso modo, per var positionals usando p.VAR_POSITIONALe var keyword con VAR_KEYWORD.

Inoltre, è possibile aggiungere una clausola a if per verificare se esiste un valore predefinito controllando se è p.defaultuguale a p.empty.


3

Estendere la risposta di DzinX:

argnames = example.func_code.co_varnames[:func.func_code.co_argcount]
args = dict((key, val) for key,val in d_args.iteritems() if key in argnames)
example(**args)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.