Question

Étant donné la fonction Python:

def aMethod(arg1, arg2):
    pass

Comment puis-je extraire le nombre et les noms des arguments? C'est-à-dire, étant donné que j'ai une référence à func, je veux que le func. [Quelque chose] retourne (& "; Arg1 &"; & "Arg2 &";).

Le scénario d'utilisation correspondant à cette situation est que j'ai un décorateur et que je souhaite utiliser les arguments de la méthode dans le même ordre que celui dans lequel ils apparaissent pour la fonction réelle en tant que clé. C'est-à-dire, à quoi ressemblerait le décorateur qui a imprimé & "; A, b &"; quand j'appelle aMethod (& "; un &"; & "; b &";)?

Était-ce utile?

La solution

Jetez un coup d'œil au module inspect - l'inspection des divers les propriétés des objets de code pour vous.

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

Les autres résultats sont le nom des variables * args et ** kwargs, ainsi que les valeurs par défaut fournies. c'est à dire.

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

Notez que certains callables peuvent ne pas être introspectables dans certaines implémentations de Python. Par exemple, dans CPython, certaines fonctions intégrées définies en C ne fournissent aucune métadonnée à propos de leurs arguments. En conséquence, vous obtiendrez ValueError dans le cas où vous utilisez inspect.getfullargspec() avec une fonction intégrée.

Depuis Python 3.3, vous pouvez également utiliser inspect.signature () afin de connaître la signature d'un objet appelable:

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

Autres conseils

Sous CPython, le nombre d'arguments est

.
aMethod.func_code.co_argcount

et leurs noms figurent au début de

aMethod.func_code.co_varnames

Ce sont des détails d'implémentation de CPython, donc cela ne fonctionne probablement pas dans d'autres implémentations de Python, telles que IronPython et Jython.

Un moyen portable d'admettre & "; Pass-through &"; arguments consiste à définir votre fonction avec la signature func(*args, **kwargs). Ceci est beaucoup utilisé dans par exemple matplotlib, où la couche d'API externe transmet de nombreux arguments de mots clés à l'API de niveau inférieur.

Dans une méthode de décorateur, vous pouvez lister les arguments de la méthode d'origine de la manière suivante:

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)

Si les **kwargs sont importants pour vous, alors ce sera un peu compliqué:

        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()

Exemple:

@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]

Voici quelque chose qui, à mon avis, fonctionnera pour ce que vous voulez, en utilisant un décorateur.

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")

Exécutez-le, vous obtiendrez le résultat suivant:

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.

Je pense que vous cherchez la méthode des locaux -


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

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

La version de Python 3 est:

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

La méthode retourne un dictionnaire contenant les arguments arg et kwargs.

Python 3.5 +:

  

DeprecationWarning: inspect.getargspec () est obsolète, utilisez inspect.signature () à la place

Si précédemment:

func_args = inspect.getargspec(function).args

Maintenant:

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

Pour tester:

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

Etant donné que nous avons la fonction 'fonction' qui prend l'argument 'arg', elle sera évaluée comme étant True, sinon comme False.

Exemple tiré de la console 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

En Python 3. +, avec l’objet Signature à portée de main, un moyen facile d’obtenir un mappage entre les noms d’argument et les valeurs, utilise la méthode bind() de Signature!

Par exemple, voici un décorateur pour imprimer une carte comme celle-ci:

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}

Renvoie une liste de noms d'arguments, prend en charge les partiels et les fonctions habituelles:

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

Mise à jour pour la réponse de Brian :

Si une fonction dans Python 3 a des arguments composés uniquement de mots clés, vous devez utiliser inspect.getfullargspec:

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

donne ceci:

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

En python 3, ci-dessous consiste à transformer *args et **kwargs en dict (utilisez OrderedDict pour python < 3.6 pour conserver les <=> commandes):

.
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

Pour mettre à jour un peu la réponse de Brian , il existe désormais un joli backport de inspect.signature que vous pouvez utiliser dans anciennes versions de python: funcsigs . Donc, ma préférence personnelle irait pour

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

def aMethod(arg1, arg2):
    pass

sig = signature(aMethod)
print(sig)

Pour le plaisir, si vous êtes intéressé à jouer avec Signature des objets et même à créer des fonctions avec des signatures aléatoires de façon dynamique, vous pouvez consulter mon makefun projet.

Qu'en est-il de dir() et vars() maintenant?

Semble faire exactement ce qui est demandé super simplement & # 8230;

Doit être appelé depuis l'étendue de la fonction.

Mais faites attention de ne pas renvoyer toutes les variables locales, assurez-vous donc de le faire au tout début de la fonction si nécessaire.

Notez également que, comme indiqué dans les commentaires, cela ne permet pas de le faire en dehors de la portée. Donc, pas exactement le scénario de OP mais correspond toujours au titre de la question. D'où ma réponse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top