2008-10-13 20 views
81

que tienen un diccionario, lo que necesito para pasar claves/valores como argumentos de palabras clave .. Por ejemplo ..¿Puede enumerar los argumentos de palabra clave que recibe una función de Python?

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

Esto funciona bien, pero si hay valores en el dict d_args que no son aceptadas por la función example, es obvio que muere .. es decir, si la función se define como ejemplo def example(kw2):

Este es un problema ya que no controlo bien la generación de la d_args, o la función example .. ambos vienen desde módulos externos, y example solo acepta algunos de los argumentos de palabra clave fr om el dict ..

Lo ideal sería que yo sólo haría

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

que probablemente va a realizar un filtrado del dict, de una lista de palabras clave-argumentos válidos, pero me preguntaba: ¿Hay una manera de programáticamente enumere los argumentos de la palabra clave que toma una función específica?

Respuesta

114

Un poco mejor que inspeccionar el objeto de código directa y la elaboración de las variables es utilizar el módulo de inspeccionar.

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

Si desea saber si su exigible con un conjunto particular de argumentos, necesita los argumentos sin defecto ya se ha especificado.Estos se puede conseguir por:

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. 

A continuación, una función para contar lo que le falta de su dict particular es:

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

Del mismo modo, para comprobar si hay argumentos válidos, utilice:

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

Y lo que una prueba completa si es exigible es:

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

(Esto es bueno solo en lo que respecta al análisis arg de Python. Ninguna verificación de tiempo de ejecución para valores no válidos en kwargs obviamente no pueden ser detectados)

+0

¡Agradable! ¡No sabía esta función! – DzinX

+0

Creo que esto merece ser la respuesta aceptada. – codeape

+1

Dado que el método que utiliza objetos de código es más o menos idéntico, ¿hay algún beneficio de haber tenido que importar un módulo más ...? – jmetz

28

Esto imprimirá nombres de todos los argumentos transitables, palabras clave y las no-palabras clave:

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

Esto se debe a que primero co_varnames siempre son parámetros (al lado son variables locales, como y en el ejemplo anterior).

Así que ahora usted podría tener una función:

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) 

que luego se podría utilizar como esto:

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

EDITAR: Una pequeña adición: si realmente necesita Solo argumentos de palabra clave de una función, puede usar el atributo func_defaults para extraerlos:

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) 

Ahora podría llamar a su función con argumentos conocidos, pero kwargs extraídos, por ejemplo .:

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

Esto asume que func utiliza ningún *args o **kwargs magia en su firma.

3

La extensión de la respuesta de 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) 
6

En 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={}) 
0

Para una solución Python 3, puede utilizar inspect.signature y el filtro de acuerdo con the kind of parameters desea saber sobre.

Tomando una función de ejemplo con posicional o palabra clave, palabras clave sólo, var parámetros posicionales y de la palabra clave var:

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

se puede crear un objeto de firma para ello:

from inspect import signature 
sig = signature(spam) 

y luego filtrar con una lista de comprensión para encontrar los detalles que necesita:

>>> # 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'] 

y, del mismo modo, para var posicionales utilizando p.VAR_POSITIONAL y palabra clave var con VAR_KEYWORD.

Además, puede agregar una cláusula al si para verificar si existe un valor predeterminado al marcar si p.default es igual a p.empty.

Cuestiones relacionadas