Ich habe eine Python-Funktion, fetch_data
die eine Remote-API aufruft, einige Daten abruft und sie in ein Antwortobjekt zurückgibt. Es sieht ein bisschen wie folgt aus:
def fetch_data(self, foo, bar, baz, **kwargs):
response = Response()
# Do various things, get some data
return response
Jetzt ist es möglich, dass in den Antwortdaten "Ich habe mehr Daten, rufe mich mit einem inkrementierten page
Parameter an, um mehr zu erhalten" steht. Daher möchte ich im Wesentlichen "The Method Call" (Funktion, Parameter) im Antwortobjekt speichern, damit ich dann eine haben kann, Response.get_more()
die die gespeicherte Funktion und die gespeicherten Parameter betrachtet und die Funktion mit (fast) derselben erneut aufruft Parameter, eine neue zurückgebenResponse
Wenn nun fetch_data
wurden definiert als fetch_data(*args, **kwargs)
ich gerade speichern könnte (fetch_data, args, kwargs)
in response
. Allerdings habe ich self
, foo
, bar
und baz
zur Sorge - ich konnte einfach speichern , (fetch_data, foo, bar, baz, kwargs)
aber das ist eine höchst unerwünschte Menge an Wiederholungen.
Im Wesentlichen versuche ich herauszufinden, wie aus einer Funktion heraus ein vollständig ausgefülltes *args
und **kwargs
einschließlich der benannten Parameter der Funktion erstellt werden kann.
Response
wenn ich die Signatur von ändern würdefetch_data
.Antworten:
Wie wäre es, wenn Sie die Argumente über
locals()
zu Beginn der Funktion speichern ?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')
Es gibt diese Ausgabe:
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'}}
Hutspitze: https://stackoverflow.com/a/3137022/2829764
quelle
Ich würde nichts tun, aber Sie könnten
inspect.getargspec
die Argumente Ihrer Methode überprüfen:>>> 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)
Dies kann dann kombiniert werden
locals()
, um Ihre Argumente neu zu erstellen :>>> def foobar(foo, bar, baz): ... return [locals()[arg] for arg in inspect.getargspec(foobar).args] ... >>> foobar(1, 2, 3) [1, 2, 3]
Sie brauchen solche Magie jedoch wirklich nur, wenn Sie Dekorateure mit erweiterten Funktionen und dergleichen ausführen. Ich denke, es ist übertrieben hier.
quelle
Ich bin nicht sicher, ob dies genau das ist, was Sie wollen, aber es
locals()
bietet ein Wörterbuch mit lokalen Variablen.>>> def foo(bar, toto): ... print(locals()) ... >>> foo(3,'sometext') {'toto': 'sometext', 'bar': 3}
quelle
Ich denke, eine pythonischere Möglichkeit besteht darin, Ihre Funktion in einen Generator zu verwandeln,
yield
Daten abzurufen und zu erfassen, solange der Server weiterhin Daten zurückgibt .Dies sollte zu ordentlichem Code führen und es Ihnen ermöglichen, alle Komplexitäten der Beibehaltung der Argumente über Iterationen hinweg zu umgehen (Python erledigt dies auf magische Weise für Sie :-)).
quelle
get_more
Methoden hinzufügen , die Ergebnisse aus der nächsten Reihe von Generatoriterationen zurückgeben.next()
/get_more()
auf dem zurückgegebenen Objekt zu haben, fühlt sich weitaus angemessener an ...next()
den Generator genauso einfach anrufen (oder nicht anrufen) . stackoverflow.com/questions/4741243/…inspect.getargspec
ist seit Version 3.0 veraltet. Usesignature()
and Signature Object, das eine bessere Introspecting-API für Callables bietet.>>> 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'>
quelle
import inspect def f(x, y): print( inspect.getargvalues(inspect.currentframe()) ) f(1, 2)
Ergebnis:
ArgInfo(args=['x', 'y'], varargs=None, keywords=None, locals={'y': 2, 'x': 1})
quelle
Ich glaube, die Methode der Wahl stammt
getcallargs
von,inspect
da sie echte Argumente zurückgibt, mit denen die Funktion aufgerufen wird. Wenn Sie eine Funktion und Argumente und Warnungen an it übergeben (inspect.getcallargs(func, /, *args, **kwds)
), werden die Argumente der realen Methode zurückgegeben, die für den Aufruf verwendet werden, wobei Standardwerte und andere Elemente berücksichtigt werden. Schauen Sie sich unten ein Beispiel an.from inspect import getcallargs # we have a function with such signature def show_params(first, second, third=3): pass # if you wanted to invoke it with such params (you could get them from a decorator as example) args = [1, 2, 5] kwargs = {} print(getcallargs(show_params, *args, **kwargs)) #{'first': 1, 'second': 2, 'third': 5} # here we didn't specify value for d args = [1, 2, 3, 4] kwargs = {} # ---------------------------------------------------------- # but d has default value =7 def show_params1(first, *second, d = 7): pass print(getcallargs(show_params1, *args, **kwargs)) # it will consider b to be equal to default value 7 as it is in real method invocation # {'first': 1, 'second': (2, 3, 4), 'd': 7} # ---------------------------------------------------------- args = [1] kwargs = {"d": 4} def show_params2(first, d=3): pass print(getcallargs(show_params2, *args, **kwargs)) #{'first': 1, 'd': 4}
https://docs.python.org/3/library/inspect.html
quelle
kwargs haben keine 'foo', 'bar' oder 'bad' als Schlüssel, also können Sie diese Einträge (mit ihren Werten) zu kwargs hinzufügen und einfach speichern (fetch_data, kwargs).
quelle