121 Stimmen

Können Sie die Schlüsselwortargumente auflisten, die eine Funktion erhält?

Ich habe ein Diktat, die ich brauche, um Schlüssel/Werte als Schlüsselwort Argumente übergeben Zum Beispiel

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

Das funktioniert gut, sondern wenn es Werte im d_args-Diktat gibt, die von der example Funktion, stirbt sie offensichtlich. Angenommen, die Beispielfunktion ist definiert als def example(kw2):

Das ist ein Problem, denn ich habe keine Kontrolle über die Erzeugung der d_args oder die example Funktion. Sie kommen beide von externen Modulen, und example akzeptiert nur einige der Schlüsselwort-Argumente aus dem dict..

Im Idealfall würde ich einfach Folgendes tun

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

Ich werde wahrscheinlich nur das Diktat aus einer Liste gültiger Schlüsselwortargumente filtern, aber ich habe mich gefragt: Gibt es eine Möglichkeit, die Schlüsselwortargumente, die eine bestimmte Funktion benötigt, programmatisch aufzulisten?

167voto

Brian Punkte 112487

Ein wenig angenehmer als das direkte Inspizieren des Codeobjekts und das Herausarbeiten der Variablen ist die Verwendung des Inspektionsmoduls.

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

Wenn Sie wissen wollen, ob die Funktion mit einem bestimmten Satz von Args aufrufbar ist, benötigen Sie die Args, für die noch kein Standardwert angegeben ist. Diese kann man erhalten durch:

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.

Dann ist eine Funktion, die Ihnen sagt, was Ihnen in Ihrem speziellen Diktat fehlt:

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

Um auf ungültige Args zu prüfen, verwenden Sie ebenfalls:

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

Ein umfassender Test, ob es abrufbar ist, ist also :

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

(Dies ist nur so weit gut, wie Python's arg parsing. Jegliche Laufzeitprüfungen auf ungültige Werte in kwargs können offensichtlich nicht erkannt werden).

0 Stimmen

Toll! Ich kannte diese Funktion nicht!

1 Stimmen

Wenn man bedenkt, dass die Methode mit den Code-Objekten mehr oder weniger identisch ist, gibt es dann einen Vorteil, wenn man ein weiteres Modul importieren muss ...?

0 Stimmen

@jmets - auf jeden Fall - es ist praktisch immer besser, ein Bibliotheksmodul zu verwenden, als ein eigenes zu entwickeln. Außerdem sind die Attribute des Code-Objekts eher interner Natur und können geändert werden (z.B. wurde es nach code in pyhon3). Die Verwendung des Moduls als Schnittstelle ist ein wenig zukunftssicherer, falls sich einige dieser Interna jemals ändern sollten. Es wird auch Dinge tun, an die du vielleicht nicht gedacht hast, wie z.B. einen entsprechenden Typfehler bei Funktionen auslösen, die du nicht inspizieren kannst (z.B. C-Funktionen).

35voto

DzinX Punkte 51124

Dies gibt die Namen aller übergebbaren Argumente aus, sowohl die der Schlüsselwörter als auch die der Nicht-Schlüsselwörter:

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

Dies liegt daran, dass zunächst co_varnames sind immer Parameter (die nächsten sind lokale Variablen, wie y im obigen Beispiel).

Jetzt könnte man also eine Funktion haben:

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)

Die Sie dann wie folgt verwenden könnten:

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

EDIT : Ein kleiner Zusatz: Wenn Sie wirklich nur Schlüsselwortargumente benötigen einer Funktion, können Sie die func_defaults Attribut, um sie zu extrahieren:

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)

Sie könnten nun Ihre Funktion mit bekannten args, aber extrahierten kwargs aufrufen, z.B.:

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

Dies setzt voraus, dass func verwendet keine *args o **kwargs Magie in seiner Signatur.

0 Stimmen

Was ist, wenn ich nur die "Schlüsselwort"-Argumente "Schlüssel" ausdrucken möchte?

0 Stimmen

In Python 3.10 verwenden Sie __code__ stattdessen func_code .

15voto

Für eine Python 3-Lösung können Sie inspect.signature und filtern nach die Art der Parameter über die Sie gerne etwas wissen möchten.

Entnahme einer Beispielfunktion mit den Parametern positional oder keyword, keyword-only, var positional und var keyword:

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

Sie können dafür ein Signaturobjekt erstellen:

from inspect import signature
sig =  signature(spam)

und filtern Sie dann mit Hilfe eines Listenverständnisses, um die gewünschten Details zu finden:

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

und in ähnlicher Weise für var-Positionale mit p.VAR_POSITIONAL und das Schlüsselwort var mit VAR_KEYWORD .

Außerdem können Sie der if-Klausel eine Klausel hinzufügen, um zu prüfen, ob ein Standardwert existiert, indem Sie prüfen, ob p.default ist gleich p.empty .

0 Stimmen

El kind Attribut ist sehr praktisch, danke!

9voto

jfs Punkte 370717

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

3voto

Claudiu Punkte 215027

Ich erweitere die Antwort von 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)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X