Tenha cuidado ao lidar com valores booleanos na argamassa de Python

O negócio

Para lidar com argumentos de linha de comando em Python, usar os módulos argv ou argparse do módulo sys.

O módulo argparse permite um tratamento flexível dos argumentos da linha de comando, mas é preciso ter cuidado ao lidar com valores booleanos (verdadeiros, falsos).

A seguinte informação é fornecida aqui.

  • argparse para uma fácil definição dos argumentos
  • Especificar o tipo do argumento (tipo) com argparse
  • Não especificar “bool” como tipo de argumento de add_argument()
  • Julgamento por bool()
  • Utilizar a acção de argumento em vez do tipo de argumento.
  • Usando a função strtobool()

argparse para uma fácil definição dos argumentos

O módulo argparse facilita a definição de argumentos de linha de comando.

O módulo argparse torna fácil a criação de interfaces de linha de comando de fácil utilização. O utilizador define que argumentos o seu programa necessita, e argparse descobrirá como analisar essas opções a partir do módulo sys.argv. argparse gera automaticamente mensagens de ajuda e utilização, e levanta um erro se o utilizador especificar argumentos inválidos para o programa. erro quando o utilizador especifica argumentos inválidos para o programa.
argparse — Parser for command-line options, arguments and sub-commands — Python 3.10.0 Documentation

Especificar o tipo do argumento (tipo) com argparse

Uma característica útil da argparse é especificar o tipo (tipo).

Por exemplo, se especificar um tipo inteiro (int), converterá automaticamente o argumento em int e também levantará um erro para argumentos que não são int.

O tipo é especificado pelo tipo de argumento de 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))

Execute este ficheiro a partir da linha de comando.

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

Argumento 100 é lido como int.

Se um valor não-pintado for utilizado como argumento, ocorrerá um 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'

Muito útil para jogar argumentos inesperados.

Não especificar “bool” como tipo de argumento de add_argument()

É importante notar que bool, como int e float, não funcionará como esperado se especificar bool como o 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))

Execute este ficheiro a partir da linha de comando.

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

Se for usado como argumento, será lido como um tipo de bool verdadeiro. Este é o comportamento esperado, mas o problema é o caso seguinte.

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

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

Se usar falso ou qualquer outro fio como argumento, ele será lido como verdadeiro.

A razão porque isto acontece é que quando o tipo=xxx é especificado no add_argument(), o argumento é passado para xxx().

Por exemplo, se type=int, o argumento será passado para int(); se type=float, então float().

O mesmo é válido para type=bool, o que significa que o argumento será passado para bool().

Julgamento por bool()

Este bool() é complicado.

Os seguintes valores são considerados falsos:

  • None
  • false
  • Zero em tipos numéricos. Por exemplo, os seguintes valores
    • 0
    • 0.0
    • 0j
  • Uma sequência vazia. Por exemplo
    • ''
    • ()
    • []
  • Cartografia vazia. Por exemplo
    • {}

Todos os outros valores são assumidos como verdadeiros – assim, objectos de muitos tipos são sempre verdadeiros. Operações e funções incorporadas que retornam resultados booleanos retornam sempre 0 ou Falso como o falso valor e 1 ou Verdadeiro como o verdadeiro valor, a menos que se note o contrário.

Portanto, todas as cordas não vazias passadas a bool(), quer sejam “verdadeiras” ou “falsas”, voltarão a ser verdadeiras. Apenas as cordas vazias serão falsas.

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

print(bool(''))
# False

Quando type=bool é definido em add_argument(), o argumento é passado para bool(). Portanto, como mostrado no exemplo acima, se falso for usado como argumento, será convertido por bool() como a string 'Falso' e lido como verdadeiro.

Utilizar a acção de argumento em vez do tipo de argumento.

Se quiser usar valores booleanos em argparse, especificar 'store_true' ou 'store_false' para a acção de argumento.

  • 'store_true'
  • 'store_false'

Estas serão versões especiais de 'store_const' que irão armazenar Verdadeiro e Falso, respectivamente. Além disso, definirão os valores por defeito para Falso e Verdadeiro respectivamente, por essa ordem.
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, são dadas as seguintes opções.
--enPortanto, se o en não for definido como verdadeiro, será carregado como falso, que é o valor por defeito do en.

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

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

Se quiser definir o padrão como verdadeiro, e falso quando a opção é adicionada, basta fazer o seguinte.
action='store_false'

Usando a função strtobool()

Se quiser usar argumentos posicionais em vez de opções, também pode usar a função strtobool().

strtobool() é uma função que converte uma corda em verdadeira (1) ou falsa (0).

Converte um fio booleano em verdadeiro (1) ou falso (0).
Os verdadeiros valores são os seguintes

  • y
  • yes
  • true
  • on
  • 1

Os valores falsos são os seguintes.

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

Se val não for nenhuma das anteriores, eleva o ValueError.

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

Não é sensível a maiúsculas e minúsculas, por exemplo, pode usar o seguinte; qualquer outra cadeia resultará num 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(), mas o valor de retorno não é bool, mas int (1 ou 0).

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

Como escrito anteriormente, quando o tipo=xxx é especificado em add_argument() de argparse, o argumento será passado para xxx(). Por conseguinte, podemos fazer 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 não é um tipo bool, mas um tipo int 1 ou 0, mas pode ler valores verdadeiros ou falsos com argumentos verdadeiros ou falsos.

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

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

Além disso, se o argumento não for esperado, será gerado um erro de forma adequada.

$ 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'
Copied title and URL