Boole değerlerini argparse ile ayrıştırma


614

"--Foo True" veya "--foo False" olarak yazılan boolean komut satırı argümanlarını ayrıştırmak için argparse kullanmak istiyorum. Örneğin:

my_program --my_boolean_flag False

Ancak, aşağıdaki test kodu ne istediğini yapmaz:

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)

Ne yazık ki, parsed_args.my_booldeğerlendirir True. Bu benim değiştirseniz bile böyledir cmd_lineolmak ["--my_bool", ""]beri, şaşırtıcı olan, bool("")hiç evalutates False.

Nasıl ayrıştırmak için argparse alabilirsiniz "False", "F"ve onların küçük harf olmak varyantları False?


40
İşte @ mgilson cevabının tek satırlık bir yorumu parser.add_argument('--feature', dest='feature', default=False, action='store_true'). Bu çözüm her zaman bir almak GARANTYSY olacak booldeğerle türünü Trueveya False. (Bu çözümün bir kısıtlaması vardır: seçeneğiniz varsayılan bir değere sahip olmalıdır.)
Trevor Boyd Smith

7
@ Maxim'in cevabının tek satırlık bir yorumu parser.add_argument('--feature', dest='feature', type=lambda x:bool(distutils.util.strtobool(x))). Seçenek kullanıldığında, bu çözüm veya booldeğeri olan bir tür sağlayacaktır . Seçenek kullanılmadığında elde edersiniz . ( başka bir TrueFalseNonedistutils.util.strtobool(x)
Trevor Boyd Smith

8
ne gibi bir şeyparser.add_argument('--my_bool', action='store_true', default=False)
AruniRC

Yanıtlar:


275

Önceki önerileri kullanan, ancak "doğru" ayrıştırma hatası ile başka bir çözüm 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.')

Bu, varsayılan değerlere sahip anahtarlar yapmak için çok kullanışlıdır; Örneğin

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

kullanmama izin veriyor:

script --nice
script --nice <bool>

ve yine de varsayılan bir değer kullanın (kullanıcı ayarlarına özgü). Bu yaklaşımın (dolaylı olarak ilişkili) bir dezavantajı, 'nargs' ın konumsal bir argüman yakalayabilmesidir - bu ilgili soruyu ve bu argparse hata raporunu görün .


4
nargs = '?' sıfır veya bir argüman anlamına gelir. docs.python.org/3/library/argparse.html#nargs
Maxim

1
Bunu seviyorum, ama benim eşdeğer varsayılan = NICE bana bir hata veriyor, bu yüzden başka bir şey yapmak gerekir.
Michael Mathews

2
@MarcelloRomani str2bool Python anlamında bir tür değildir, yukarıda tanımlanan işlevdir, bir yere dahil etmeniz gerekir.
Maxim

4
kodu str2bool(v)ile değiştirilebilir bool(distutils.util.strtobool(v)). Kaynak: stackoverflow.com/a/18472142/2436175
Antonio

4
Belki de bu şekilde, eğer argüman if args.nice:False olarak ayarlanırsa, argümanın beacuse ile ayarlanıp ayarlanmadığını kontrol edemeyeceğinizi belirtmek gerekir . Bu sağa sonra ise belki de listeyi dönmek daha iyidir str2boolfonksiyonu gibi grup listesindeki constböyle parametre, [True], [False].
Yanlışsam

887

Bunu yapmak için daha kanonik bir yol olduğunu düşünüyorum:

command --feature

ve

command --no-feature

argparse güzel bu sürümü destekler:

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

Tabii ki, --arg <True|False>sürümü gerçekten istiyorsanız ast.literal_eval, "tip" veya kullanıcı tanımlı bir işlev olarak geçebilirsiniz ...

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?

96
Hala type=boolkutunun dışında çalışması gerektiğini düşünüyorum (konumsal argümanları düşünün!). Ek olarak belirttiğinizde bile choices=[False,True], hem "False" hem de "True" ifadesinin True olarak kabul edildiğini (dizeden boole aktarılmasından dolayı?). Belki ilgili sorun
yunus

41
Doğru, bence bu beklendiği gibi çalışmıyor. Güvenlik kontrolleri veya hata mesajları olmadığından bu son derece yanıltıcıdır.
yunus

69
@mgilson - Ya yanıltıcı bulmak olduğunu o olabilir type = bool set, size nedeniyle nasıl birine (sözde boolean değişkeni Doğru olsun, hem "Yanlış" ve "Doğru" dizesi argümanlar için henüz hiçbir hata mesajı almak ve tip döküm işleri python). Bu nedenle, type = bool açıkça desteklenmemelidir (bazı uyarı, hata, vb. Yayar) veya yararlı ve sezgisel olarak beklenen bir şekilde çalışmalıdır.
yunus

14
@ dolphin - sırasıyla, katılmıyorum. Ben davranış tam olması gerektiği gibi olduğunu ve "Özel durumlar kuralları ihlal edecek kadar özel değildir" python zen ile tutarlı olduğunu düşünüyorum. Ancak, bunu bu konuda güçlü bir şekilde hissediyorsanız, neden çeşitli python posta listelerinden birini getirmiyorsunuz ? Orada, bu konuda bir şeyler yapma gücüne sahip birini ikna etme şansınız olabilir . Beni ikna edebilseniz bile, beni sadece ikna etmeyi başaracaksınız ve bir geliştirici olmadığım için davranış hala değişmeyecek :)
mgilson

15
Python bool()işlevinin ne yapması gerektiği ya da hangi argparse'ın kabul etmesi gerektiği konusunda tartışıyor muyuz type=fn? Tüm argparsekontroller fnçağrılabilir olmasıdır. fnBir dize argümanı almayı ve bir değer döndürmeyi bekler . Davranışı fnprogramcının sorumluluğundadır, değil argparse's.
hpaulj

235

Ben ancak bir birbirini dışlayan grupla mgilson yanıtını tavsiye
Kullanmadığınız böylece --featureve --no-featureaynı anda.

command --feature

ve

command --no-feature

Ama değil

command --feature --no-feature

Senaryo:

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)

Daha sonra, birçoğunu ayarlayacaksanız bu yardımcıyı kullanabilirsiniz:

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

5
@CharlieParker add_argumentile çağrılır dest='feature'. set_defaultsile çağrılır feature=True. Anlama?
fnkr

4
Bu veya mgilson'un cevabı kabul edilen cevap olmalıdır - OP istese de --flag False, SO cevaplarının bir kısmı sadece NASIL değil, ne çözmeye çalıştıklarıyla ilgili olmalıdır. Kesinlikle yapmak için hiçbir neden olmamalı --flag Falseveya --other-flag Truedaha sonra dizeyi bir boole dönüştürmek için bazı özel ayrıştırıcı kullanılmalıdır .. action='store_true've action='store_false'boolean bayraklarını kullanmanın en iyi yolları vardır
kevlarr

6
@cowlinator Neden SO nihayetinde "belirtildiği gibi soruları" yanıtlamakla ilgilidir? Göre kendi kurallarına bir anwer ... can be “don’t do that”, but it should also include “try this instead”(bana en azından) cevaplarını ima uygun olduğunda derin gitmek gerekir. Bazılarımızın soru göndererek daha iyi / en iyi uygulamalar, vb. Rehberlikten faydalanabileceği kesinlikle zamanlar vardır. Bununla birlikte, genellikle çok fazla (veya yanlış) olduğunu varsayan cevaplarla ilgili hayal kırıklığınız tamamen geçerlidir.
kevlarr

2
Kullanıcı özelliği açıkça belirtmediğinde üçüncü bir değere sahip olmak istiyorsa, son satırıparser.set_defaults(feature=None)
Alex Che

2
help=Bu argüman için bir girdi eklemek istiyorsak nereye gitmeliyiz? Gelen add_mutually_exclusive_group()çağrı? add_argument()Çağrıların birinde veya her ikisinde mi? Başka bir yer?
Ken Williams

57

Varsayılan değerleri ayarlamak için fazladan satır / satır olmayan başka bir varyasyon. Boole her zaman atanmış bir değere sahiptir, böylece ön denetimler olmadan mantıksal ifadelerde kullanılabilir.

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("Check that args.do_something=" + str(args.do_something) + " is always a bool")

5
Bu cevabın hafife alınması, ancak sadeliği harika. Ayarlamaya çalışmayın required=Trueyoksa her zaman Gerçek bir argüman alırsınız.
Garren S

1
Lütfen ASLA bool veya nonetype gibi şeylerde eşitlik operatörü kullanmayın. Bunun yerine IS'yi kullanmalısınız
webKnjaZ

2
Bu, kabul edilenden daha iyi bir yanıttır, çünkü gereksiz boole dizesi gerektirmek yerine, boole değerini ayarlamak için bayrağın varlığını kontrol eder. (Hey, seni booleanlar gibi duydum ... bu yüzden sana booleanı ayarlamak için boolean ile bir boolean verdim!)
Sifon

4
Hmm ... soru, belirtildiği gibi, komut satırının kendisinde "Doğru" / "Yanlış" kullanmak istiyor gibi görünüyor; ancak bu örnekle python3 test.py --do-something Falsebaşarısız olur error: unrecognized arguments: False, bu yüzden soruya gerçekten cevap vermez.
sdbbs

38

oneliner:

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

4
oneliner fanı için iyi, ayrıca biraz geliştirilebilir:type=lambda x: (str(x).lower() in ['true','1', 'yes'])
Tu Bui

35

Ne anlama gelebileceği type=boolve type='bool'ne anlama gelebileceği konusunda bir karışıklık var gibi görünüyor . Biri (veya her ikisi) 'işlevi çalıştırmalı mı bool(), yoksa' bir boole döndürmelidir 'anlamına mı gelmelidir? Durduğu gibi type='bool'hiçbir şey ifade etmez. kullandığınız gibi add_argumentbir 'bool' is not callablehata verir type='foobar'veya type='int'.

Ancak argparseböyle anahtar kelimeleri tanımlamanızı sağlayan kayıt defteri var. Çoğunlukla kullanılır action, örneğin `` action = 'store_true'. Kayıtlı anahtar kelimeleri aşağıdakilerle görebilirsiniz:

parser._registries

sözlük görüntüleyen

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

Tanımlanmış birçok eylem var, ancak yalnızca bir tür, varsayılan eylem argparse.identity.

Bu kod bir 'bool' anahtar kelimesini tanımlar:

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()belgelenmemiştir, aynı zamanda gizli değildir. Çoğunlukla programcı çünkü bunu bilmek gerekmez typeve actionalmak fonksiyonu ve sınıf değerleri. Her ikisi için özel değerler tanımlayan birçok yığın akışı örneği vardır.


Önceki tartışmada açıkça görülmüyorsa, bool()'bir dizeyi ayrıştır' anlamına gelmez. Python belgelerinden:

bool (x): Standart doğruluk testi prosedürünü kullanarak bir değeri Boole değerine dönüştürün.

Bunu kontrastla

int (x): Bir sayı veya dize x'i bir tam sayıya dönüştürür.


3
Veya şu şekilde kullanın: parser.register ('type', 'bool', (lambda x: x.lower (), ("yes", "true", "t", "1")))
Matyas

17

Aynı sorunu arıyordum ve güzel çözüm imho:

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

ve bunu yukarıda önerildiği gibi boole dizesini ayrıştırmak için kullanma.


5
Bu rotaya gidecekseniz, önerebilir miyim distutils.util.strtobool(v)?
CivFan

1
distutils.util.strtoboolDöner, 1 veya 0, gerçek bir Boole.
CMCDragonkai

14

Oldukça benzer bir yol kullanmaktır:

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

ve komutunuzda --feature argümanını ayarlarsanız

 command --feature

argüman Doğru olacaktır, tür - özellik belirtmezseniz varsayılan bağımsız değişkenler her zaman Yanlış'tır!


1
Bu yöntemin diğer cevapların üstesinden gelmesinin bir dezavantajı var mı? Bu, OP'nin (ve bu durumda benim) ne istediğine ulaşan en kolay, en özlü çözüm gibi görünüyor. Onu seviyorum.
Simon O'Hanlon

2
Basit olsa da, soruya cevap vermez. OP belirtebileceğiniz bir argüman istiyorum--feature False
Astariul


12

Bu beklediğim her şey için çalışır:

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

Kod:

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

Mükemmel! Bu cevap ile gidiyorum. Bir kez _str_to_bool(s)dönüştürmek s = s.lower(), sonra test etmek if s not in {'true', 'false', '1', '0'}ve son olarak benim tweaked return s in {'true', '1'}.
Jerry101

6

Daha basit bir yol aşağıdaki gibi kullanmak olacaktır.

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

5

En basit. Esnek değil, ama basitliği tercih ediyorum.

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

EDIT: Eğer girdiye güvenmiyorsanız, kullanmayın eval.


Bu oldukça uygun görünüyor. Tür olarak eval olduğunu fark ettim. Bu konuda bir sorum vardı: eval nasıl tanımlanmalı, ya da bundan faydalanmak için bir ithalat gerekli mi?
edesz

1
evalyerleşik bir işlevdir. docs.python.org/3/library/functions.html#eval Bu, daha esnek yaklaşımların avantaj sağladığı herhangi bir tekli işlev olabilir.
Russell

Hey, bu harika. Teşekkürler!
edesz

2
bu sevimli, ancak kötülük olduğunu bilmeyen kullanıcıların sadece senaryolarına kopyalayıp yapıştıracağı vahşi doğada ortaya çıkarmak oldukça risklidir .
Arne

@ Arne, iyi bir nokta. Bununla birlikte, iyi niyetli bir kullanıcının yanlışlıkla zararlı bir şey yapmasının oldukça zor olduğu görülüyor.
Russell

3

En basit yol seçenekleri kullanmak olabilir :

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

args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)

- bayrağım iletilmezse False olarak değerlendirilir. Gerekli = Gerçek her zaman açıkça bir seçim belirtmek için kullanıcı istiyorsanız seçeneği eklenebilir.


2

En kanonik yol olacağını düşünüyorum:

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

ENSURE = config.ensure is None

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

1

En basit ve en doğru yol

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

Doğru değerlerin y, evet, t, doğru, açık ve 1 olduğunu unutmayın; false değerleri n, no, f, false, off ve 0'dır. val başka bir şeyse ValueError değerini yükseltir.


0

Hızlı ve kolay, ancak yalnızca 0 veya 1 bağımsız değişkenleri için:

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

Terminalden çağrıldıktan sonra çıkış "Yanlış" olacaktır:

python myscript.py 0

-1

@Akash'a benzer, ancak burada kullandığım başka bir yaklaşım var. Bu kullanır strdaha lambdapiton çünkü lambdaher zaman beni bir yabancı-duyguları verir.

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

-1

@Akash Desarda'nın cevabına bir gelişme olarak,

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)

Ve destekler 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.