quelle est la bonne façon de traiter Python argparse.Namespace () comme un dictionnaire?

228

Si je veux utiliser les résultats de argparse.ArgumentParser(), qui est un Namespaceobjet, avec une méthode qui attend un dictionnaire ou un objet semblable à un mappage (voir collections.Mapping ), quelle est la bonne façon de le faire?

C:\>python
Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> import argparse
>>> args = argparse.Namespace()
>>> args.foo = 1
>>> args.bar = [1,2,3]
>>> args.baz = 'yippee'
>>> args['baz']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Namespace' object has no attribute '__getitem__'
>>> dir(args)
['__class__', '__contains__', '__delattr__', '__dict__', '__doc__', '__eq__', '_
_format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__
', '__str__', '__subclasshook__', '__weakref__', '_get_args', '_get_kwargs', 'ba
r', 'baz', 'foo']

Est-il approprié de "pénétrer" dans un objet et d'utiliser sa __dict__propriété?

Je pense que la réponse est non: __dict__sent comme une convention pour la mise en œuvre, mais pas pour une interface, la manière __getattribute__ou __setattr__ou __contains__semble être.

Jason S
la source

Réponses:

385

Vous pouvez accéder au dictionnaire de l'espace de noms avec vars () :

>>> import argparse
>>> args = argparse.Namespace()
>>> args.foo = 1
>>> args.bar = [1,2,3]
>>> d = vars(args)
>>> d
{'foo': 1, 'bar': [1, 2, 3]}

Vous pouvez modifier le dictionnaire directement si vous le souhaitez:

>>> d['baz'] = 'store me'
>>> args.baz
'store me'

Oui, vous pouvez accéder à l'attribut __dict__. Il s'agit d'un comportement bien défini, testé et garanti.

Raymond Hettinger
la source
1
Les documents disent: "Le dictionnaire renvoyé ne doit pas être modifié: les effets sur la table de symboles correspondante ne sont pas définis." Ce qui ne peut faire référence qu'au comportement de vars()(qui est soit locals()ou globals()), mais je ne suis pas vraiment sûr.
2
hmm, je suppose que je ne comprends pas vraiment la différence entre utiliser vars()et__dict__
Jason S
21
@delnan Quelqu'un a fait une modification incorrecte des documents et fait un avertissement trop large. Les documents ont ensuite été corrigés. Voir docs.python.org/2.7/library/functions.html#vars Bien qu'il existe certains cas spéciaux qui ont des dictionnaires en lecture seule (tels que les sections locales et les proxys de dictionnaire de classe), le reste des cas peut être mis à jour. L' appel vars (obj) est synonyme d'obj .__ dict__. Dans le cas d'un espace de noms argparse , vars (args) donne un accès direct à un dictionnaire pouvant être mis à jour.
Raymond Hettinger
1
@RaymondHettinger D'accord, soigné. J'ai obtenu cette note de la /3/version des documents (à y regarder de plus près, 3.1 à 3.4 y compris), donc la correction y est apparemment manquante.
8
@delnan Je viens de mettre à jour les documents 3.3 et 3.3. Il sera visible demain. Merci de l'avoir signalé.
Raymond Hettinger
64

Directement de la bouche du cheval :

Si vous préférez avoir une vue de type dict des attributs, vous pouvez utiliser l'idiome Python standard vars():

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> args = parser.parse_args(['--foo', 'BAR'])
>>> vars(args)
{'foo': 'BAR'}

- La bibliothèque standard Python, 16.4.4.6. L'objet Namespace

Eugene Yarmash
la source
1
'Foo' manque toujours '-'. Une idée à ça?
user2678074
1
@ user2678074 ceci est intentionnel pour faire correspondre les drapeaux du shell. Les tirets sont superflus à l'intérieur de l'exécution de python.
Erich
vars(args)me donneTypeError: 'dict' object is not callable
user5359531
6
@ user5359531 vous avez probablement écrasé le global varsavec une variable. Vous pouvez utiliser __builtins__.varspour y accéder directement ou del varspour arrêter de le masquer.
Nick T
@NickT Correction mineure: les fonctions Python ont une portée statique, donc si vous omettez une variable globale dans une fonction, cet identifiant se référera toujours à la variable locale dans cette fonction, avant même qu'elle ne soit affectée ou après del. Au niveau de la portée du module, del il fonctionnera pour «dé-masquer» les buildins.
augurar
-1

Est-il approprié de "toucher" un objet et d'utiliser sa propriété dict ?

En général, je dirais "non". Cependant, cela Namespacem'a paru trop complexe, peut-être à partir du moment où les classes ne pouvaient pas hériter des types intégrés.

D'un autre côté, Namespaceprésente une approche de l'argparse axée sur les tâches, et je ne peux pas penser à une situation qui nécessiterait de saisir le __dict__, mais les limites de mon imagination ne sont pas les mêmes que les vôtres.

msw
la source
11
Il est parfaitement correct d'accéder à l'attribut __dict__. L'introspection est fondamentale pour la langue. L'attribut a été rendu public pour une raison :-)
Raymond Hettinger
8
Mais tout en Python est "public". Il n'y a pas de distinction (à l'exception de la convention de soulignement principale) entre les variables d'implémentation utilisées dans une instance et l'interface publique qu'elle présente. Surtout dans un objet de type dictionnaire: la ligne entre les méthodes d'instance et les valeurs de dictionnaire qui sont des fonctions est un peu floue.
Jason S
Si vous passez les arguments en tant que paramètres nommés? do_something(**args.__dict__)
OrangeDog