2010-01-18 3 views
13

Si j'ai une fonction python comme ceci:Get Inside kwargs Fonction

def some_func(arg1, arg2, arg3=1, arg4=2): 

est-il un moyen de déterminer quels arguments ont été adoptés par mot-clé à l'intérieur de la fonction?

EDIT

Pour ceux qui demandent pourquoi j'ai besoin, je n'ai aucune raison réelle, il est venu dans une conversation et la curiosité a obtenu le meilleur de moi.

+1

Je ne pense pas ... pourquoi s'embêter avec ** kw alors? – jldupont

+0

Je suis désolé, mais si c'est juste votre curiosité, je suis en train de voter pour fermer, il n'y a pas vraiment de problème, pas de cas d'utilisation, et personne n'aura jamais de problème dont vous rêvez. – SilentGhost

+0

En fait, j'ai rencontré ce problème une fois. J'écrivais une fonction pour générer un wrapper XML (ajouter une balise start et ajouter une balise end) autour de CDATA, et je voulais que ça ressemble à 'def wrap (balise, contenu = None, ** attrs):', mais ensuite a couru tête baissée dans le problème que les attributs peuvent avoir des caractères que les identifiants Python ne peuvent pas. Mais c'était un exercice intéressant pour quelques minutes. –

Répondre

14

Non, il n'y a pas moyen de le faire dans le code Python avec cette signature - si vous avez besoin de ces informations, vous devez changer la signature de la fonction. Si vous regardez l'API C de Python, vous verrez que la manière dont les arguments sont passés à une fonction Python normale est toujours un tuple plus un dict, c'est-à-dire la manière directe d'une signature de *args, **kwargs. Ce tuple et dict sont ensuite analysés dans des arguments positionnels spécifiques et ceux qui sont nommés dans la signature même s'ils ont été passés par nom, et les *a et **kw, si présents, ne prennent le "débordement" de cette analyse, le cas échéant - C'est seulement à ce moment-là que votre code Python prend le contrôle, et d'ici là, l'information que vous demandez (comment les ont été passés) n'est plus disponible. Pour obtenir l'information que vous avez demandée, changez donc la signature en *a, **kw et faites votre propre analyse/validation - cela va "de l'oeuf à l'omelette", c'est-à-dire un certain travail mais certainement faisable, ce que vous cherchez serait "de l'omelette à l'œuf" ... tout simplement impossible ;-).

+0

+1: analogie intéressante! – jldupont

+0

Pas vraiment, ça m'a fait faim :( – gorsky

1

voulez-vous savoir si arg3 était 1 parce qu'il a été transmis de l'extérieur ou parce qu'il s'agissait d'une valeur par défaut? Non, il n'y a aucun moyen de le faire pour autant que je sache. La raison principale, je soupçonne, qu'il n'y a pas besoin d'une telle connaissance. Ce qui est généralement fait est le suivant:

>>> def func(a, b=None): 
    if b is None: 
# here we know that function was called as: 
# func('spam') or func('spam', None) or func('spam', b=None) or func(a='spam', b=None) 

     b = 42 
1

suffit de le faire comme ceci:

def some_func (arg1, arg2, arg3=None, arg4=None): 
    if arg3 is None: 
     arg3 = 1 # default value 
    if arg4 is None: 
     arg4 = 2 # default value 

    # do something 

De cette façon, vous pouvez voir quand quelque chose a été mis, et vous êtes également capable de travailler avec des structures par défaut plus complexes (comme les listes) sans se heurter à des problèmes tels que ceux-ci:

>>> def test(arg=[]): 
     arg.append(1) 
     print(arg) 
>>> test() 
[1] 
>>> test() 
[1, 1] 
+0

Mais vous ne pouvez toujours pas faire la différence entre 'some_func (1, 2, None, 4)' et 'some_func (1, 2, arg4 = 4)', par exemple. –

+1

Pourquoi avez-vous besoin de cela? Les arguments nommés sont de sorte que vous pouvez laisser d'autres paramètres facultatifs (donc vous n'avez pas à tous les écrire) – poke

+0

Eh bien, c'est la question, n'est-ce pas? personne qui peut répondre à cette question –

2

Vous êtes à peu près devoir redéfinir votre fonction:

def some_func(*args, **kwargs): 

et faites le marshaling vous-même. Il n'y a aucun moyen de faire la différence entre la position par défaut, le mot de passe par passe et la valeur par défaut.

9

Voici ma solution par les décorateurs:

def showargs(function): 
    def inner(*args, **kwargs): 
     return function((args, kwargs), *args, **kwargs) 
    return inner 

@showargs 
def some_func(info, arg1, arg2, arg3=1, arg4=2): 
    print arg1,arg2,arg3,arg4 
    return info 

In [226]: some_func(1,2,3, arg4=4) 
1 2 3 4 
Out[226]: ((1, 2, 3), {'arg4': 4}) 

Il peut y avoir un moyen de nettoyer cette place plus loin, mais cela me semble peu intrusive et ne nécessite aucune modification du code d'appel.

Edit: Pour tester réellement si args particulières ont été adoptées par mot-clé, puis faire quelque chose comme ce qui suit à l'intérieur de some_func:

args, kwargs = info 
if 'arg4' in kwargs: 
    print "arg4 passed as keyword argument" 

Avertissement: vous devriez probablement envisager si vous vraiment ou non comment les arguments ont été passés. Toute cette approche peut être inutile.

+0

aussi local() je pense –