Booleaanse waarden ontleden met argparse

Ik wil argparse gebruiken om booleaanse opdrachtregelargumenten te ontleden die zijn geschreven als “–foo True” of “–foo False”. Bijvoorbeeld:

my_program --my_boolean_flag False

De volgende testcode doet echter niet wat ik zou willen:

import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", type=bool)
cmd_line = ["--my_bool", "False"]
parsed_args = parser.parse(cmd_line)

Helaas evalueert parsed_args.my_booltot True. Dit is zelfs het geval wanneer ik cmd_lineverander in ["--my_bool", ""], wat verrassend is, aangezien bool("")evalueert tot False.

Hoe kan ik ervoor zorgen dat argparse "False", "F"en hun varianten in kleine letters om Falsete ontleden?


Antwoord 1, autoriteit 100%

Nog een andere oplossing die de vorige suggesties gebruikt, maar met de “juiste” parseerfout van argparse:

def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

Dit is erg handig om schakelaars te maken met standaardwaarden; bijvoorbeeld

parser.add_argument("--nice", type=str2bool, nargs='?',
                        const=True, default=False,
                        help="Activate nice mode.")

kan ik gebruiken:

script --nice
script --nice <bool>

en nog steeds een standaardwaarde gebruiken (specifiek voor de gebruikersinstellingen). Een (indirect gerelateerd) nadeel van die aanpak is dat de ‘nargs’ een positioneel argument kunnen vangen — zie deze gerelateerde vraagen dit argparse-bugrapport.


Antwoord 2, autoriteit 95%

Ik denk dat een meer canonieke manier om dit te doen is via:

command --feature

en

command --no-feature

argparseondersteunt deze versie goed:

parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)

Als u echt de --arg <True|False>-versie wilt, kunt u natuurlijk ast.literal_evaldoorgeven als het “type”, of een door de gebruiker gedefinieerde functie …

def t_or_f(arg):
    ua = str(arg).upper()
    if 'TRUE'.startswith(ua):
       return True
    elif 'FALSE'.startswith(ua):
       return False
    else:
       pass  #error condition maybe?

Antwoord 3, autoriteit 74%

Als je --featureen --no-featuretegelijkertijd wilt toestaan (laatste wint)

Hiermee kunnen gebruikers een shell-alias maken met --featureen deze overschrijven met --no-feature.

Python 3.9 en hoger

parser.add_argument('--feature', default=True, action=argparse.BooleanOptionalAction)

Python 3.8 en lager

Ik raad het antwoord van mgilson aan:

parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)

Als u --featureen --no-featureNIET tegelijkertijd wilt toestaan

U kunt een wederzijds uitsluitende groep gebruiken:

feature_parser = parser.add_mutually_exclusive_group(required=False)
feature_parser.add_argument('--feature', dest='feature', action='store_true')
feature_parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)

Je kunt deze helper gebruiken als je er veel wilt instellen:

def add_bool_arg(parser, name, default=False):
    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--' + name, dest=name, action='store_true')
    group.add_argument('--no-' + name, dest=name, action='store_false')
    parser.set_defaults(**{name:default})
add_bool_arg(parser, 'useful-feature')
add_bool_arg(parser, 'even-more-useful-feature')

Antwoord 4, autoriteit 24%

Hier is een andere variatie zonder extra rij / s om standaardwaarden in te stellen. De Booleaanse waarde wordt altijd toegewezen, zodat deze in logische verklaringen kan worden gebruikt zonder vooraf te controleren:

import argparse
parser = argparse.ArgumentParser(description="Parse bool")
parser.add_argument("--do-something", default=False, action="store_true",
                    help="Flag to do something")
args = parser.parse_args()
if args.do_something:
     print("Do something")
else:
     print("Don't do something")
print(f"Check that args.do_something={args.do_something} is always a bool.")

5, Autoriteit 15%

Oneliner:

parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))

6, Autoriteit 9%

Er lijkt enige verwarring te zijn over wat type=boolen type='bool'zou kunnen betekenen. Moet een (of beide) betekenen ‘Run de functie bool()of’ retourneer een boolean ‘? Zoals het staat type='bool'betekent niets. add_argumentGeeft een 'bool' is not callableFOUT, hetzelfde als u gebruikte type='foobar', of type='int'.

Maar argparseheeft register waarmee u zoekwoorden als deze kunt definiëren. Het wordt meestal gebruikt voor action, b.v. `Actie = ‘Store_true’. U kunt de geregistreerde trefwoorden bekijken met:

parser._registries

die een woordenboek weergeeft

{'action': {None: argparse._StoreAction,
  'append': argparse._AppendAction,
  'append_const': argparse._AppendConstAction,
...
 'type': {None: <function argparse.identity>}}

Er zijn veel acties gedefinieerd, maar slechts één type, de standaard, argparse.identity.

Deze code definieert een ‘bool’-zoekwoord:

def str2bool(v):
  #susendberg's function
  return v.lower() in ("yes", "true", "t", "1")
p = argparse.ArgumentParser()
p.register('type','bool',str2bool) # add type keyword to registries
p.add_argument('-b',type='bool')  # do not use 'type=bool'
# p.add_argument('-b',type=str2bool) # works just as well
p.parse_args('-b false'.split())
Namespace(b=False)

parser.register()is niet gedocumenteerd, maar ook niet verborgen. Voor het grootste deel hoeft de programmeur er niets van te weten omdat typeen actionfunctie- en klassewaarden aannemen. Er zijn veel stackoverflow-voorbeelden van het definiëren van aangepaste waarden voor beide.


In het geval dat het niet duidelijk is uit de vorige discussie, bool()betekent niet ‘een string ontleden’. Uit de Python-documentatie:

bool(x): Converteer een waarde naar een Boolean, met behulp van de standaard waarheidstestprocedure.

Vergelijk dit met

int(x): Converteer een getal of tekenreeks x naar een geheel getal.


Antwoord 7, autoriteit 6%

Een vergelijkbare manier is om:

feature.add_argument('--feature',action='store_true')

en als je het argument –feature in je commando zet

command --feature

het argument zal True zijn, als u geen type –feature instelt, zijn de standaardargumenten altijd False!


Antwoord 8, autoriteit 4%

Ik was op zoek naar hetzelfde probleem, en ik denk dat de mooie oplossing is:

def str2bool(v):
  return v.lower() in ("yes", "true", "t", "1")

en dat gebruiken om de string te ontleden naar boolean, zoals hierboven gesuggereerd.


Antwoord 9, autoriteit 3%

Eenvoudigste & meest correcte manier is:

from distutils.util import strtobool
parser.add_argument('--feature', dest='feature', 
                    type=lambda x: bool(strtobool(x)))

Houd er rekening mee dat True-waarden y, yes, t, true, on en 1 zijn;
false waarden zijn n, no, f, false, off en 0. Verhoogt ValueError als val iets anders is.


Antwoord 10, autoriteit 3%

Naast wat @mgilson zei, moet worden opgemerkt dat er ook een ArgumentParser.add_mutually_exclusive_group(required=False)methode die het triviaal zou maken om af te dwingen dat --flagen --no-flagzijn’ t tegelijkertijd gebruikt.


Antwoord 11, autoriteit 3%

Dit werkt voor alles wat ik ervan verwacht:

add_boolean_argument(parser, 'foo', default=True)
parser.parse_args([])                   # Whatever the default was
parser.parse_args(['--foo'])            # True
parser.parse_args(['--nofoo'])          # False
parser.parse_args(['--foo=true'])       # True
parser.parse_args(['--foo=false'])      # False
parser.parse_args(['--foo', '--nofoo']) # Error

De code:

def _str_to_bool(s):
    """Convert string to bool (in argparse context)."""
    if s.lower() not in ['true', 'false']:
        raise ValueError('Need bool; got %r' % s)
    return {'true': True, 'false': False}[s.lower()]
def add_boolean_argument(parser, name, default=False):                                                                                               
    """Add a boolean argument to an ArgumentParser instance."""
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        '--' + name, nargs='?', default=default, const=True, type=_str_to_bool)
    group.add_argument('--no' + name, dest=name, action='store_false')

Antwoord 12, autoriteit 2%

Het eenvoudigst. Het is niet flexibel, maar ik geef de voorkeur aan eenvoud.

 parser.add_argument('--boolean_flag',
                      help='This is a boolean flag.',
                      type=eval, 
                      choices=[True, False], 
                      default='True')

EDIT:Als je de invoer niet vertrouwt, gebruik dan geen eval.


Antwoord 13, autoriteit 2%

Een eenvoudigere manier zou zijn om te gebruiken zoals hieronder.

parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])

Antwoord 14

De eenvoudigste manier is om keuzeste gebruiken:

parser = argparse.ArgumentParser()
parser.add_argument('--my-flag',choices=('True','False'))
args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)

Niet doorgeven –my-flag evalueert naar False. De optie required=Truekan worden toegevoegd als u altijd wilt dat de gebruiker een keuze expliciet opgeeft.


Antwoord 15

Ik denk dat de meest canonieke manier zal zijn:

parser.add_argument('--ensure', nargs='*', default=None)
ENSURE = config.ensure is None

Antwoord 16

Als een verbetering van het antwoord van @Akash Desarda, zou je kunnen doen

import argparse
from distutils.util import strtobool
parser = argparse.ArgumentParser()
parser.add_argument("--foo", 
    type=lambda x:bool(strtobool(x)),
    nargs='?', const=True, default=False)
args = parser.parse_args()
print(args.foo)

En het ondersteunt python test.py --foo

(base) [costa@costa-pc code]$ python test.py
False
(base) [costa@costa-pc code]$ python test.py --foo 
True
(base) [costa@costa-pc code]$ python test.py --foo True
True
(base) [costa@costa-pc code]$ python test.py --foo False
False

Antwoord 17

Snel en gemakkelijk, maar alleen voor argumenten 0 of 1:

parser.add_argument("mybool", default=True,type=lambda x: bool(int(x)))
myargs=parser.parse_args()
print(myargs.mybool)

De uitvoer zal “False” zijn na aanroepen vanaf terminal:

python myscript.py 0

Antwoord 18

class FlagAction(argparse.Action):
    # From http://bugs.python.org/issue8538
    def __init__(self, option_strings, dest, default=None,
                 required=False, help=None, metavar=None,
                 positive_prefixes=['--'], negative_prefixes=['--no-']):
        self.positive_strings = set()
        self.negative_strings = set()
        for string in option_strings:
            assert re.match(r'--[A-z]+', string)
            suffix = string[2:]
            for positive_prefix in positive_prefixes:
                self.positive_strings.add(positive_prefix + suffix)
            for negative_prefix in negative_prefixes:
                self.negative_strings.add(negative_prefix + suffix)
        strings = list(self.positive_strings | self.negative_strings)
        super(FlagAction, self).__init__(option_strings=strings, dest=dest,
                                         nargs=0, const=None, default=default, type=bool, choices=None,
                                         required=required, help=help, metavar=metavar)
    def __call__(self, parser, namespace, values, option_string=None):
        if option_string in self.positive_strings:
            setattr(namespace, self.dest, True)
        else:
            setattr(namespace, self.dest, False)

19

vergelijkbaar met @akash maar hier is een andere benadering die ik heb gebruikt. Het gebruikt strdan lambdaomdat Python lambdaaltijd een buitenaardse gevoelens geeft.

import argparse
from distutils.util import strtobool
parser = argparse.ArgumentParser()
parser.add_argument("--my_bool", type=str, default="False")
args = parser.parse_args()
if bool(strtobool(args.my_bool)) is True:
    print("OK")

20

Doe gewoon het volgende, u kunt --test = Truemaken door

te gebruiken

Python-bestandsnaam – Test

parser.add_argument("--test" , default=False ,help="test ?", dest='test', action='store_true')

21

Converteer de waarde:

def __arg_to_bool__(arg):
    """__arg_to_bool__
        Convert string / int arg to bool
    :param arg: argument to be converted
    :type arg: str or int
    :return: converted arg
    :rtype: bool
    """
    str_true_values = (
        '1',
        'ENABLED',
        'ON',
        'TRUE',
        'YES',
    )
    str_false_values = (
        '0',
        'DISABLED',
        'OFF',
        'FALSE',
        'NO',
    )
    if isinstance(arg, str):
        arg = arg.upper()
        if arg in str_true_values:
            return True
        elif arg in str_false_values:
            return False
    if isinstance(arg, int):
        if arg == 1:
            return True
        elif arg == 0:
            return False
    if isinstance(arg, bool):
        return arg
    # if any other value not covered above, consider argument as False
    # or you could just raise and error
    return False
[...]
args = ap.parse_args()
my_arg = options.my_arg
my_arg = __arg_to_bool__(my_arg)

Other episodes