Domanda

Data la funzione Python:

def aMethod(arg1, arg2):
    pass

Come posso estrarre il numero e i nomi degli argomenti.I. e., dato che ho un riferimento a func, voglio il func.[qualcosa] per tornare ("arg1", "arg2").

Lo scenario di utilizzo è che ho un decoratore, e vorrei utilizzare il metodo argomenti nello stesso ordine in cui appaiono per la funzione reale di chiave.I. e., come sarebbe il decoratore look che stampate "a,b" quando l'ho chiamata aMethod("a", "b")?

È stato utile?

Soluzione

Date un'occhiata al ispezionare modulo - questo permetterà di fare l'ispezione dei vari codice proprietà dell'oggetto per voi.

>>> inspect.getfullargspec(aMethod)
(['arg1', 'arg2'], None, None, None)

Gli altri risultati sono: il nome del *args e **kwargs variabili, e le impostazioni predefinite fornite.ie.

>>> def foo(a,b,c=4, *arglist, **keywords): pass
>>> inspect.getfullargspec(foo)
(['a', 'b', 'c'], 'arglist', 'keywords', (4,))

Nota che alcuni callable non può essere introspectable in alcune implementazioni di Python.Per Esempio, in CPython, alcune built-in funzioni definite in C non forniscono i metadati delle proprie argomentazioni.Come risultato, si ottiene ValueError nel caso in cui inspect.getfullargspec() con un built-in funzione.

Dato che Python 3.3, è possibile utilizzare anche il ispezionare.firma() per conoscere la chiamata firma di un oggetto callable:

>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>

Altri suggerimenti

In CPython, il numero di argomenti è

aMethod.func_code.co_argcount

e i loro nomi sono all'inizio di

aMethod.func_code.co_varnames

Questi sono i dettagli di implementazione di CPython, quindi probabilmente questo non funziona in altre implementazioni di Python, come IronPython e Jython.

Un modo portatile per ammettere " pass-through " l'argomento è definire la tua funzione con la firma func(*args, **kwargs). Questo è molto usato ad es. matplotlib, dove il livello API esterno passa molti argomenti di parole chiave all'API di livello inferiore.

In un metodo decoratore, puoi elencare gli argomenti del metodo originale in questo modo:

import inspect, itertools 

def my_decorator():

        def decorator(f):

            def wrapper(*args, **kwargs):

                # if you want arguments names as a list:
                args_name = inspect.getargspec(f)[0]
                print(args_name)

                # if you want names and values as a dictionary:
                args_dict = dict(itertools.izip(args_name, args))
                print(args_dict)

                # if you want values as a list:
                args_values = args_dict.values()
                print(args_values)

Se **kwargs sono importanti per te, sarà un po 'complicato:

        def wrapper(*args, **kwargs):

            args_name = list(OrderedDict.fromkeys(inspect.getargspec(f)[0] + kwargs.keys()))
            args_dict = OrderedDict(list(itertools.izip(args_name, args)) + list(kwargs.iteritems()))
            args_values = args_dict.values()

Esempio:

@my_decorator()
def my_function(x, y, z=3):
    pass


my_function(1, y=2, z=3, w=0)
# prints:
# ['x', 'y', 'z', 'w']
# {'y': 2, 'x': 1, 'z': 3, 'w': 0}
# [1, 2, 3, 0]

Ecco qualcosa che penso funzionerà per quello che vuoi, usando un decoratore.

class LogWrappedFunction(object):
    def __init__(self, function):
        self.function = function

    def logAndCall(self, *arguments, **namedArguments):
        print "Calling %s with arguments %s and named arguments %s" %\
                      (self.function.func_name, arguments, namedArguments)
        self.function.__call__(*arguments, **namedArguments)

def logwrap(function):
    return LogWrappedFunction(function).logAndCall

@logwrap
def doSomething(spam, eggs, foo, bar):
    print "Doing something totally awesome with %s and %s." % (spam, eggs)


doSomething("beans","rice", foo="wiggity", bar="wack")

Eseguilo, produrrà il seguente output:

C:\scripts>python decoratorExample.py
Calling doSomething with arguments ('beans', 'rice') and named arguments {'foo':
 'wiggity', 'bar': 'wack'}
Doing something totally awesome with beans and rice.

Penso che quello che stai cercando sia il metodo dei locali -


In [6]: def test(a, b):print locals()
   ...: 

In [7]: test(1,2)              
{'a': 1, 'b': 2}

La versione di Python 3 è:

def _get_args_dict(fn, args, kwargs):
    args_names = fn.__code__.co_varnames[:fn.__code__.co_argcount]
    return {**dict(zip(args_names, args)), **kwargs}

Il metodo restituisce un dizionario contenente sia args che kwargs.

Python 3.5+:

  

DeprecationWarning: inspect.getargspec () è deprecato, utilizzare inspect.signature () invece

Quindi in precedenza:

func_args = inspect.getargspec(function).args

Ora:

func_args = list(inspect.signature(function).parameters.keys())

Per testare:

'arg' in list(inspect.signature(function).parameters.keys())

Dato che abbiamo la funzione 'funzione' che prende l'argomento 'arg', questo valuterà come Vero, altrimenti come Falso.

Esempio dalla console di Python:

Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] on win32
>>> import inspect
>>> 'iterable' in list(inspect.signature(sum).parameters.keys())
True

In Python 3. + con l'oggetto Signature a portata di mano, un modo semplice per ottenere una mappatura tra i nomi degli argomenti ai valori, sta usando il metodo bind() della firma!

Ad esempio, ecco un decoratore per stampare una mappa del genere:

import inspect

def decorator(f):
    def wrapper(*args, **kwargs):
        bound_args = inspect.signature(f).bind(*args, **kwargs)
        bound_args.apply_defaults()
        print(dict(bound_args.arguments))

        return f(*args, **kwargs)

    return wrapper

@decorator
def foo(x, y, param_with_default="bars", **kwargs):
    pass

foo(1, 2, extra="baz")
# This will print: {'kwargs': {'extra': 'baz'}, 'param_with_default': 'bars', 'y': 2, 'x': 1}

Restituisce un elenco di nomi di argomenti, si occupa dei parziali e delle funzioni regolari:

def get_func_args(f):
    if hasattr(f, 'args'):
        return f.args
    else:
        return list(inspect.signature(f).parameters)

Aggiornamento per Risposta di Brian :

Se una funzione in Python 3 ha argomenti solo per parole chiave, è necessario utilizzare inspect.getfullargspec:

def yay(a, b=10, *, c=20, d=30):
    pass
inspect.getfullargspec(yay)

produce questo:

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={})

In Python 3, di seguito è necessario trasformare *args e **kwargs in dict (utilizzare OrderedDict per Python < 3.6 per mantenere <=> gli ordini):

from functools import wraps

def display_param(func):
    @wraps(func)
    def wrapper(*args, **kwargs):

        param = inspect.signature(func).parameters
        all_param = {
            k: args[n] if n < len(args) else v.default
            for n, (k, v) in enumerate(param.items()) if k != 'kwargs'
        }
        all_param .update(kwargs)
        print(all_param)

        return func(**all_param)
    return wrapper

Per aggiornare un po ' la risposta di Brian , ora c'è un bel backport di inspect.signature che puoi usare in versioni precedenti di Python: funcsigs . Quindi la mia preferenza personale andrebbe per

try:  # python 3.3+
    from inspect import signature
except ImportError:
    from funcsigs import signature

def aMethod(arg1, arg2):
    pass

sig = signature(aMethod)
print(sig)

Per divertimento, se ti interessa giocare con Signature oggetti e persino creare funzioni con firme casuali in modo dinamico puoi dare un'occhiata al mio makefun progetto.

Che dire di dir() e vars() adesso?

Sembra fare esattamente ciò che viene chiesto semplicemente & # 8230;

Deve essere chiamato all'interno dell'ambito della funzione.

Ma fai attenzione che restituirà tutte variabili locali, quindi assicurati di farlo all'inizio della funzione, se necessario.

Si noti inoltre che, come sottolineato nei commenti, ciò non consente di farlo al di fuori dell'ambito. Quindi non esattamente lo scenario di OP, ma corrisponde ancora al titolo della domanda. Da qui la mia risposta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top