Ottenere parametro metodo di nomi in Python
-
03-07-2019 - |
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")?
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.