Teña coidado ao tratar con valores booleanos no argparse de Python

Negocios

Para xestionar argumentos da liña de comandos en Python, use os módulos argv ou argparse do módulo sys.

O módulo argparse permite un manexo flexible dos argumentos da liña de comandos, pero hai que ter coidado ao tratar con valores booleanos (true, false).

A seguinte información ofrécese aquí.

  • argparse para facilitar a definición de argumentos
  • Especifique o tipo de argumento (tipo) con argparse
  • Non especifique “bool” como tipo de argumento de add_argument()
  • Xuízo por bool()
  • Use a acción de argumento en lugar do tipo de argumento.
  • Usando a función strtobool().

argparse para facilitar a definición de argumentos

O módulo argparse facilita a definición de argumentos da liña de comandos.

O módulo argparse facilita a creación de interfaces de liña de comandos amigables. Vostede define que argumentos necesita o seu programa e argparse descubrirá como analizar esas opcións desde sys.argv. O módulo argparse xera automaticamente mensaxes de axuda e uso e xera un erro se o usuario especifica argumentos non válidos para o programa. erro cando o usuario especifica argumentos non válidos para o programa.
argparse — Parser for command-line options, arguments and sub-commands — Python 3.10.0 Documentation

Especifique o tipo de argumento (tipo) con argparse

Unha característica útil de argparse é especificar o tipo (tipo).

Por exemplo, se especifica un tipo enteiro (int), converterá automaticamente o argumento en int e tamén provocará un erro para os argumentos que non sexan int.

O tipo é especificado polo tipo de argumento add_argument().

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('arg_int', type=int)

args = parser.parse_args()
print(args.arg_int)
print(type(args.arg_int))

Executar este ficheiro desde a liña de comandos.

$ python argparse_type_int.py 100
100
<type 'int'>

O argumento 100 lese como int.

Se se usa un valor non int como argumento, producirase un erro.

$ python argparse_type_int.py foo
usage: argparse_type_int.py [-h] arg_int
argparse_type_int.py: error: argument arg_int: invalid int value: 'foo'

$ python argparse_type_int.py 1.23
usage: argparse_type_int.py [-h] arg_int
argparse_type_int.py: error: argument arg_int: invalid int value: '1.23'

Moi útil para xogar argumentos inesperados.

Non especifique “bool” como tipo de argumento de add_argument()

É importante ter en conta que bool, como int e float, non funcionará como se esperaba se especificas bool como tipo de argumento de add_argument().

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('arg_bool', type=bool)

args = parser.parse_args()
print(args.arg_bool)
print(type(args.arg_bool))

Executar este ficheiro desde a liña de comandos.

$ python argparse_type_bool.py True
True
<type 'bool'>

Se se usa true como argumento, lerase como un tipo bool true. Este é o comportamento esperado, pero o problema é o seguinte caso.

$ python argparse_type_bool.py False
True
<type 'bool'>

$ python argparse_type_bool.py bar
True
<type 'bool'>

Se usas false ou calquera outra cadea como argumento, lerase como verdadeiro.

O motivo polo que isto ocorre é que cando se especifica type=xxx en add_argument(), o argumento pásase a xxx().

Por exemplo, se type=int, o argumento pasarase a int(); se type=float, entón float().

O mesmo ocorre con type=bool, o que significa que o argumento pasará a bool().

Xuízo por bool()

Este bool() é complicado.

Os seguintes valores considéranse falsos:

  • None
  • false
  • Cero en tipos numéricos. Por exemplo, os seguintes valores
    • 0
    • 0
    • 0j
  • Unha secuencia baleira. Por exemplo
    • ()
    • []
  • Mapeo baleiro. Por exemplo
    • {}

Suponse que todos os demais valores son verdadeiros; polo tanto, os obxectos de moitos tipos son sempre verdadeiros. As operacións e funcións integradas que devolven resultados booleanos sempre devolven 0 ou False como valor falso e 1 ou True como verdadeiro, a non ser que se indique o contrario.

Polo tanto, todas as cadeas non baleiras pasadas a bool(), xa sexan “true” ou “false”, devolverán true. Só as cadeas baleiras serán falsas.

print(bool('True'))
print(bool('False'))
print(bool('abc'))
# True
# True
# True

print(bool(''))
# False

Cando se establece type=bool en add_argument(), o argumento pásase a bool(). Polo tanto, como se mostra no exemplo anterior, se se usa false como argumento, bool() converterase como cadea ‘False’ e lerase como verdadeiro.

Use a acción de argumento en lugar do tipo de argumento.

Se queres usar valores booleanos en argparse, especifique ‘store_true’ ou ‘store_false’ para a acción do argumento.

  • store_true’
  • store_false’

Estas serán versións especiais de ‘store_const’ que almacenarán True e False respectivamente. Ademais, establecerán os valores predeterminados en False e True respectivamente, nesa orde.
argparse — Parser for command-line options, arguments and sub-commands — Python 3.10.0 Documentation

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--en', action='store_true')

args = parser.parse_args()
print(args.en)
print(type(args.en))

Neste exemplo, ofrécense as seguintes opcións.
--enPolo tanto, se en non se define como verdadeiro, cargarase como falso, que é o valor predeterminado de en.

$ python argparse_option_bool.py --en
True
<type 'bool'>

$ python argparse_option_bool.py
False
<type 'bool'>

Se queres establecer o valor predeterminado como verdadeiro e falso cando se engade a opción, fai o seguinte.
action='store_false'

Usando a función strtobool().

Se queres usar argumentos posicionais en lugar de opcións, tamén podes usar a función strtobool().

strtobool() é unha función que converte unha cadea en verdadeiro (1) ou falso (0).

Converte unha cadea booleana en verdadeiro (1) ou falso (0).
Os verdadeiros valores son os seguintes

  • y
  • yes
  • true
  • on
  • 1

Os valores falsos son os seguintes.

  • n
  • no
  • f
  • false
  • off
  • 0

Se val non é ningunha das anteriores, xera ValueError.

9. API Reference – strtobool() — Python 3.10.0 Documentation

Non distingue entre maiúsculas e minúsculas, polo que, por exemplo, pode usar o seguinte; calquera outra cadea producirá un erro.

  • TRUE'
  • True'
  • YES'
from distutils.util import strtobool

print(strtobool('true'))
print(strtobool('True'))
print(strtobool('TRUE'))
# 1
# 1
# 1

print(strtobool('t'))
print(strtobool('yes'))
print(strtobool('y'))
print(strtobool('on'))
print(strtobool('1'))
# 1
# 1
# 1
# 1
# 1

print(strtobool('false'))
print(strtobool('False'))
print(strtobool('FALSE'))
# 0
# 0
# 0

print(strtobool('f'))
print(strtobool('no'))
print(strtobool('n'))
print(strtobool('off'))
print(strtobool('0'))
# 0
# 0
# 0
# 0
# 0

# print(strtobool('abc'))
# ValueError: invalid truth value 'abc'

O nome é strtobool(), pero o valor de retorno non é bool, senón int (1 ou 0).

print(type(strtobool('true')))
# <class 'int'>

Como se escribiu anteriormente, cando se especifica type=xxx en add_argument() de argparse, o argumento pasarase a xxx(). Polo tanto, podemos facer o seguinte.
type=strtobool

import argparse
from distutils.util import strtobool

parser = argparse.ArgumentParser()
parser.add_argument('arg_bool', type=strtobool)

args = parser.parse_args()
print(args.arg_bool)
print(type(args.arg_bool))

O valor de retorno non é un tipo bool, senón un tipo int 1 ou 0, pero pode ler valores verdadeiros ou falsos con true ou false como argumentos.

$ python argparse_type_strtobool.py true
1
<type 'int'>

$ python argparse_type_strtobool.py false
0
<type 'int'>

Ademais, se non se espera o argumento, xerarase un erro correctamente.

$ python argparse_type_strtobool.py bar
usage: argparse_type_strtobool.py [-h] arg_bool
argparse_type_strtobool.py: error: argument arg_bool: invalid strtobool value: 'bar'