2012-05-23 12 views
20

Tengo una función de Python, fetch_data, que va y golpea una API remota, toma algunos datos y los devuelve envueltos en un objeto de respuesta. Se ve un poco como el siguiente:Obtener todos los argumentos y valores pasados ​​a una función

def fetch_data(self, foo, bar, baz, **kwargs): 
    response = Response() 
    # Do various things, get some data 
    return response 

Ahora, es posible que los datos de respuesta dice "tengo más datos, me llama con un parámetro page incrementado a conseguir más". Por lo tanto, esencialmente me gustaría almacenar "The Method Call" (función, parámetros) en el objeto de respuesta, entonces puedo tener un Response.get_more() que examina la función y los parámetros almacenados, y llama a la función nuevamente con (casi) el los mismos parámetros, que vuelven una nueva Response

Ahora bien, si fetch_data se definieron como fetch_data(*args, **kwargs) tan sólo pudiera almacenar (fetch_data, args, kwargs) en response. Sin embargo, tengo que preocuparme por self, foo, bar y baz, podría simplemente almacenar (fetch_data, foo, bar, baz, kwargs), pero esa es una cantidad de repetición muy indeseable.

Básicamente, estoy tratando de encontrar la forma de, dentro de una función, obtener un *args y **kwargs completamente poblados, incluyendo los parámetros nombrados de la función.

+0

¿Por qué no pasar foo, bar, baz y kwargs al constructor de Response() para que luego llame a response.get_more() ya tenga esos valores? – kurosch

+0

Porque eso no soluciona el problema; aún tendría que tocar 'Response' si cambié la firma de' fetch_data'. –

+0

Quizás eso sea algo bueno? – kurosch

Respuesta

8

Creo que una manera más Pythonic es convertir su función en un generador, obteniendo datos y yield mientras el servidor siga devolviendo cosas.

Esto debe resultar en código limpio y que le permiten esquivar todas las complejidades de la preservación de los argumentos a través de iteraciones (Python mágicamente lo haga por usted :-))

+2

Este es definitivamente uno de los casos que es mucho más adecuado para dicha aplicación. Y de esa forma también le da a OP la oportunidad de agregar el método 'get_more' que devuelve los resultados de la siguiente serie de iteraciones de generador. – Tadeck

+0

El caso es que hay muchos casos en los que no te importaría que hubiera más datos, por lo que devolver un generador se siente un poco antinatural; tener un 'next()'/'get_more()' en el objeto devuelto se siente mucho más apropiado ... –

+0

@KristianGlass: No estoy seguro de seguir. Puede simplemente llamar (o no) '' next() 'en el generador. http://stackoverflow.com/questions/4741243/how-to-pick-just-one-item-from-a-generator-in-python – NPE

0

kwargs no tendrán ' foo ',' bar 'o' bad 'como claves, por lo que puede agregar esas entradas (con sus valores) a kwargs y simplemente almacenar (fetch_data, kwargs).

+0

Ese es el mismo nivel de duplicación, pero de una manera diferente ... –

15
No

algo que me gustaría hacer, pero se puede usar inspect.getargspec a inspeccionar los argumentos que su método tiene:

>>> import inspect 
>>> def foobar(foo, bar, baz): 
...  return inspect.getargspec(foobar) 
... 
>>> foobar(1, 2, 3) 
ArgSpec(args=['foo', 'bar', 'baz'], varargs=None, keywords=None, defaults=None) 

Esto se puede combinar con locals() para reconstruir sus argumentos:

>>> def foobar(foo, bar, baz): 
...  return [locals()[arg] for arg in inspect.getargspec(foobar).args] 
... 
>>> foobar(1, 2, 3) 
[1, 2, 3] 

Sin embargo, realmente solo necesitas tal magia cuando haces decoradores de funciones avanzadas y similares. Creo que es excesivo aquí.

31

Básicamente, estoy tratando de averiguar cómo, dentro de una función, obtener un * args y ** kwargs completamente poblados, incluidos los parámetros nombrados de la función.

¿Qué le parece guardar los argumentos a través del locals() al principio de la función?

def my_func(a, *args, **kwargs): 
    saved_args = locals() 
    print("saved_args is", saved_args) 
    local_var = 10 
    print("saved_args is", saved_args) 
    print("But locals() is now", locals()) 

my_func(20, 30, 40, 50, kwarg1='spam', kwarg2='eggs') 

Se da este resultado: punta

saved_args is {'a': 20, 'args': (30, 40, 50), 'kwargs': {'kwarg1': u'spam', 'kwarg2': u'eggs'}} 
saved_args is {'a': 20, 'args': (30, 40, 50), 'kwargs': {'kwarg1': u'spam', 'kwarg2': u'eggs'}} 
But locals is now {'a': 20, 'saved_args': {...}, 'args': (30, 40, 50), 'local_var': 10, 'kwargs': {'kwarg1': u'spam', 'kwarg2': u'eggs'}} 

Hat: https://stackoverflow.com/a/3137022/2829764

2

inspect.getargspec está obsoleta desde la versión 3.0.Use signature() y Signature Object, que proporcionan una mejor API de introspección para callables.

>>> from inspect import signature 
>>> def foo(a, *, b:int, **kwargs): 
...  pass 

>>> sig = signature(foo) 

>>> str(sig) 
'(a, *, b:int, **kwargs)' 

>>> str(sig.parameters['b']) 
'b:int' 

>>> sig.parameters['b'].annotation 
<class 'int'> 
Cuestiones relacionadas