web-dev-qa-db-fra.com

Argparse booléen optionnel

J'essaie d'obtenir le comportement suivant:

  • python test.py => store foo = False
  • python test.py --foo => store foo = True
  • python test.py --foo bool => stocker foo = bool

Ça marche quand j'utilise

    parser.add_argument('--foo',nargs='?', default=False, const=True)

Cependant ça casse si j'ajoute type=bool, en essayant d'imposer le casting à booléen. Dans ce cas

python test.py --foo False

En fait, finit par stocker foo=True. Que se passe-t-il??

6
Hyperplane

Êtes-vous sûr que vous avez besoin de ce modèle? --foo Et --foo <value>, Ensemble, pour un commutateur booléen, n'est pas un modèle courant à utiliser.

Quant à votre problème, rappelez-vous que la valeur de la ligne de commande est une chaîne et que type=bool Signifie que vous voulez bool(entered-string-value) à appliquer. Pour --foo False, Cela signifie bool("False"), produisant True; toutes les chaînes non vides sont vraies! Voir Pourquoi argparse n'analyse-t-il pas correctement mon drapeau booléen? également.

Au lieu de prendre en charge --foo/--foo <string value>, Je fortement vous recommande d'utiliser --foo Pour signifier True, supprimez la valeur de l'argument et ajoutez à la place une option --no-foo pour définir explicitement False:

parser.add_argument('--foo', default=False, action='store_true')
parser.add_argument('--no-foo', dest='foo', action='store_false')

L'ajout de dest='foo' Sur le commutateur --no-foo Garantit que la valeur False qu'il stocke (via store_false) Se retrouve sur le même attribut args.foo .

Vous n'auriez besoin d'une combinaison --foo / --no-foo Que si vous avez un autre mécanisme de configuration qui définirait foo sur True et que vous deviez le remplacer à nouveau avec un commutateur de ligne de commande. --no-<option> Est une norme largement adoptée pour inverser un commutateur de ligne de commande booléen.

Si vous n'avez pas un besoin spécifique pour un interrupteur inversé --no-foo (Car juste en omettant --foo Signifierait déjà 'faux'), alors restez avec l'option action='store_true'. Cela rend votre ligne de commande simple et claire!

Cependant, si votre cas d'utilisation ou d'autres contraintes nécessitent spécifiquement que votre ligne de commande doive avoir un certain support de --foo (true|false|0|1), alors ajoutez votre propre convertisseur:

def str_to_bool(value):
    if isinstance(value, bool):
        return value
    if value.lower() in {'false', 'f', '0', 'no', 'n'}:
        return False
    Elif value.lower() in {'true', 't', '1', 'yes', 'y'}:
        return True
    raise ValueError(f'{value} is not a valid boolean value')

parser.add_argument('--foo', type=str_to_bool, nargs='?', const=True, default=False)
  • la valeur const est utilisée pour les arguments nargs='?' où la valeur d'argument est omise. Ici, cela définit foo=True Lorsque --foo Est utilisé.
  • default=False Est utilisé lorsque le commutateur n'est pas utilisé du tout.
  • type=str_to_bool Est utilisé pour gérer le cas --foo <value>.

Démo:

$ cat so52403065.py
from argparse import ArgumentParser

parser = ArgumentParser()

def str_to_bool(value):
    if value.lower() in {'false', 'f', '0', 'no', 'n'}:
        return False
    Elif value.lower() in {'true', 't', '1', 'yes', 'y'}:
        return True
    raise ValueError(f'{value} is not a valid boolean value')

parser.add_argument('--foo', type=str_to_bool, nargs='?', const=True, default=False)

print(parser.parse_args())
$ python so52403065.py
Namespace(foo=False)
$ python so52403065.py --foo
Namespace(foo=True)
$ python so52403065.py --foo True
Namespace(foo=True)
$ python so52403065.py --foo no
Namespace(foo=False)
$ python so52403065.py --foo arrbuggrhellno
usage: so52403065.py [-h] [--foo [FOO]]
so52403065.py: error: argument --foo: invalid str_to_bool value: 'arrbuggrhellno'
12
Martijn Pieters

Vous devez utiliser le action='store_true' paramètre à la place pour les arguments booléens:

parser.add_argument('--foo', action='store_true')

Pour que l'absence de --foo option:

python test.py

entraînerait une valeur False pour l'argument foo et la présence de --foo option:

python test.py --foo

entraînerait une valeur True pour l'argument foo.

4
blhsing