Maison >développement back-end >Tutoriel Python >Utilisez Click pour créer le programme de ligne de commande Python parfait

Utilisez Click pour créer le programme de ligne de commande Python parfait

WBOY
WBOYavant
2023-04-18 14:55:031249parcourir

Utilisez Click pour créer le programme de ligne de commande Python parfait


La tâche principale des programmeurs Python est d'écrire des programmes en ligne de commande, c'est-à-dire des scripts qui s'exécutent directement dans le terminal. Au fur et à mesure que le projet grandit, nous espérons créer une interface de ligne de commande efficace capable de résoudre différents problèmes en fournissant différents paramètres au lieu de modifier le code source à chaque fois.

Afin d'atteindre cet objectif, j'ai résumé quatre principes, j'espère que cela sera utile à tout le monde :

  • Les paramètres de ligne de commande doivent fournir des valeurs par défaut
  • Gérer toutes les erreurs de paramètres possibles, y compris les paramètres manquants, les données erronées type, et introuvable Documents, etc.
  • Rédigez une bonne documentation qui explique la signification des paramètres et comment les définir
  • Utilisez une barre de progression pour afficher les tâches de longue durée

Un exemple simple

Appliquons ces règles à un cas concret : utiliser un script de chiffrement César pour chiffrer et déchiffrer des messages.

Supposons que nous écrivions une fonction de chiffrement comme indiqué ci-dessous. Créons maintenant un script pour crypter et déchiffrer les messages.

Le script permet à l'utilisateur de sélectionner : le mode (cryptage ou déchiffrement), la clé. La valeur par défaut du premier est le cryptage et la valeur par défaut du second est 1. Tout cela est réalisé grâce aux paramètres de ligne de commande.

def encrypt(plaintext, key):
cyphertext = ''
for character in plaintext:
if character.isalpha():
number = ord(character)
number += key
if character.isupper():
if number > ord('Z'):
number -= 26
elif number < ord('A'):
number += 26
elif character.islower():
if number > ord('z'):
number -= 26
elif number < ord('a'):
number += 26
character = chr(number)
cyphertext += character
return cyphertext

Méthode du débutant : sys.argv

Le script doit d'abord obtenir la valeur du paramètre de ligne de commande, utilisons le sys.argv le plus simple pour l'implémenter en premier.

sys.argv est une liste qui contient tous les paramètres saisis par l'utilisateur lors de l'exécution du script (y compris le nom du script lui-même).

Entrez la commande suivante dans le terminal :

> python caesar_script.py --key 23 --decrypt my secret message
pb vhfuhw phvvdjh

sys.argv list comprend :

['caesar_script.py', '--key', '23', '--decrypt', 'my', 'secret', 'message']

Pour obtenir la valeur du paramètre, vous devez parcourir la liste des paramètres et rechercher une '--key' (ou '-k') pour obtenir la valeur de la clé et recherchez un mode d'obtention '--decrypt'.

import sys
from caesar_encryption import encryp
def caesar():
key = 1
is_error = False
for index, arg in enumerate(sys.argv):
if arg in ['--key', '-k'] and len(sys.argv) > index + 1:
key = int(sys.argv[index + 1])
del sys.argv[index]
del sys.argv[index]
break
for index, arg in enumerate(sys.argv):
if arg in ['--encrypt', '-e']:
del sys.argv[index]
break
if arg in ['--decrypt', '-d']:
key = -key
del sys.argv[index]
break
if len(sys.argv) == 1:
is_error = True
else:
for arg in sys.argv:
if arg.startswith('-'):
is_error = True
if is_error:
print(f'Usage: python {sys.argv[0]} [ --key <key> ] [ --encrypt|decrypt ] <text>')
else:
print(encrypt(' '.join(sys.argv[1:]), key))
if __name__ == '__main__':
caesar()

Le code suit les principes que nous avons exposés au début :

Avoir une valeur de clé par défaut et un mode par défaut

Gérer les erreurs de base (pas de texte de saisie fourni ou paramètres inconnus)

En cas d'erreurs de paramètres ou sans paramètres Lors de l'appel du script, imprimez un message d'invite concis

> python caesar_script_using_sys_argv.py
Usage: python caesar.py [ --key <key> ] [ --encrypt|decrypt ] <text>

Mais cette version du script est assez longue (39 lignes, sans compter la fonction de cryptage), et le code est très moche.

Existe-t-il une meilleure façon d'analyser les arguments de ligne de commande ?

Entrez argparse

argparse est un module de bibliothèque standard Python pour analyser les arguments de ligne de commande.

Modifiez le script pour utiliser argparse pour analyser les arguments de ligne de commande :

import argparse
from caesar_encryption import encrypt
def caesar():
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-e', '--encrypt', action='store_true')
group.add_argument('-d', '--decrypt', action='store_true')
parser.add_argument('text', nargs='*')
parser.add_argument('-k', '--key', type=int, default=1)
args = parser.parse_args()
text_string = ' '.join(args.text)
key = args.key
if args.decrypt:
key = -key
cyphertext = encrypt(text_string, key)
print(cyphertext)
if __name__ == '__main__':
caesar()

Le code adhère toujours aux principes que nous avons proposés et fournit une documentation plus précise et une gestion des erreurs plus interactive que l'analyse manuelle des arguments de ligne de commande.

> python caesar_script_using_argparse.py --encode My message
usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]
caesar_script_using_argparse.py: error: unrecognized arguments: --encode
> python caesar_script_using_argparse.py --help
usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]positional arguments:
text
optional arguments:
-h, --help show this help message and exit
-e, --encrypt
-d, --decrypt
-k KEY, --key KEY

Les lignes 7 à 13 du script définissent des arguments de ligne de commande, mais ils ne sont pas très élégants : trop verbeux et procéduraux, on peut le faire de manière plus compacte et déclarative.

Utilisez click pour créer une meilleure interface de ligne de commande

Heureusement, il existe une bibliothèque tierce click pour créer des interfaces de ligne de commande. Elle fournit non seulement plus de fonctions que argparse, mais a également un style de code plus beau. Remplacez argparse par click et continuez à optimiser le script.

import click
from caesar_encryption import encrypt
@click.command()
@click.argument('text', nargs=-1)
@click.option('--decrypt/--encrypt', '-d/-e')
@click.option('--key', '-k', default=1)
def caesar(text, decrypt, key):
text_string = ' '.join(text)
if decrypt:
key = -key
cyphertext = encrypt(text_string, key)
click.echo(cyphertext)
if __name__ == '__main__':
caesar()

Notez que les arguments et options de ligne de commande sont déclarés dans les décorateurs, ce qui les rend directement accessibles en tant qu'arguments aux fonctions.

Analysons attentivement le code ci-dessus :

nargs définit le nombre de valeurs reçues par l'argument de ligne de commande. La valeur par défaut est 1, nargs=-1 permet de fournir n'importe quel nombre de mots.

--encrypt/--decrypt définit des options mutuellement exclusives, qui sont finalement transmises au programme sous forme de valeur booléenne.

click.echo est la fonction de base fournie par la bibliothèque de clics. Sa fonction est similaire à l'impression, mais fournit des fonctions plus puissantes, comme l'ajustement de la couleur du texte imprimé sur la console.

Lire l'entrée du fichier local

La valeur reçue par l'argument de ligne de commande est un message top secret qui sera crypté, il peut donc soulever des problèmes de sécurité si l'utilisateur est invité à saisir du texte brut directement dans le terminal.

Un moyen plus sûr consiste à utiliser des astuces cachées ou à lire le texte à partir d'un fichier local, ce qui est plus pratique pour les textes longs.

La même idée s'applique à la sortie : l'utilisateur peut l'enregistrer dans un fichier ou l'imprimer dans le terminal. Continuons à optimiser le script.

import click
from caesar_encryption import encrypt
@click.command()
@click.option(
'--input_file',
type=click.File('r'),
help='File in which there is the text you want to encrypt/decrypt.'
 'If not provided, a prompt will allow you to type the input text.',
)
@click.option(
'--output_file',
type=click.File('w'),
help='File in which the encrypted / decrypted text will be written.'
 'If not provided, the output text will just be printed.',
)
@click.option(
'--decrypt/--encrypt',
'-d/-e',
help='Whether you want to encrypt the input text or decrypt it.'
)
@click.option(
'--key',
'-k',
default=1,
help='The numeric key to use for the caesar encryption / decryption.'
)
def caesar(input_file, output_file, decrypt, key):
if input_file:
text = input_file.read()
else:
text = click.prompt('Enter a text', hide_input=not decrypt)
if decrypt:
key = -key
cyphertext = encrypt(text, key)
if output_file:
output_file.write(cyphertext)
else:
click.echo(cyphertext)
if __name__ == '__main__':
caesar()

Au fur et à mesure que le script devient plus complexe, nous créons un document de paramètres (implémenté en définissant le paramètre d'aide du décorateur click.option) pour expliquer la fonction du paramètre en détail. L'effet est le suivant.

> python caesar_script_v2.py --help
Usage: caesar_script_v2.py [OPTIONS]
Options:
--input_file FILENAMEFile in which there is the text you want to encrypt/decrypt. If not provided, a prompt will allow you to type the input text.
--output_file FILENAME File in which the encrypted/decrypted text will be written. If not provided, the output text will just be printed.
-d, --decrypt / -e, --encryptWhether you want to encrypt the input text or decrypt it.
-k, --key INTEGERThe numeric key to use for the caesar encryption / decryption.
--help Show this message and exit.

Nous avons deux nouveaux paramètres input_file et output_file, le type est click.File, click ouvrira le fichier dans le bon mode et gérera les erreurs possibles. Par exemple, le fichier est introuvable :

> python caesar_script_v2.py --decrypt --input_file wrong_file.txt
Usage: caesar_script_v2.py [OPTIONS]
Error: Invalid value for "--input_file": Could not open file: wrong_file.txt: No such file or directory

Si input_file n'est pas fourni, nous utilisons click.prompt pour créer une fenêtre d'invite sur la ligne de commande afin de permettre à l'utilisateur de saisir directement du texte. L'invite sera masquée pour le cryptage. mode. L'effet est le suivant :

> python caesar_script_v2.py --encrypt --key 2
Enter a text: **************
yyy.ukectc.eqo

假设你是一名黑客:想要解密一个用凯撒加密过的密文,但你不知道秘钥是什么。最简单的策略就是用所有可能的秘钥调用解密函数 25 次,阅读解密结果,看看哪个是合理的。但你很聪明,而且也很懒,所以你想让整个过程自动化。确定解密后的 25 个文本哪个最可能是原始文本的方法之一,就是统计所有这些文本中的英文单词的个数。这可以使用 PyEnchant 模块实现:

import click
import enchant
from caesar_encryption import encrypt
@click.command()
@click.option(
'--input_file',
type=click.File('r'),
required=True,
)
@click.option(
'--output_file',
type=click.File('w'),
required=True,
)
def caesar_breaker(input_file, output_file):
cyphertext = input_file.read()
english_dictionnary = enchant.Dict("en_US")
max_number_of_english_words = 0
for key in range(26):
plaintext = encrypt(cyphertext, -key)
number_of_english_words = 0
for word in plaintext.split(' '):
if word and english_dictionnary.check(word):
number_of_english_words += 1
if number_of_english_words > max_number_of_english_words:
max_number_of_english_words = number_of_english_words
best_plaintext = plaintext
best_key = key
click.echo(f'The most likely encryption key is {best_key}. It gives the following plaintext:nn{best_plaintext[:1000]}...')
output_file.write(best_plaintext)
if __name__ == '__main__':
caesar_breaker()

Utilisez Click pour créer le programme de ligne de commande Python parfait

使用进度条

示例中的文本包含10^4个单词,因此该脚本需要大约5秒才能解密。这很正常,因为它需要检查所有25个秘钥,每个秘钥都要检查10^4个单词是否出现在英文字典中。

假设你要解密的文本包括10^5个单词,那么就要花费50秒才能输出结果,用户可能会非常着急。因此我建议这种任务一定要显示进度条。特别是,显示进度条还非常容易实现。下面是个显示进度条的例子:

import click
import enchant
from tqdm import tqdm
from caesar_encryption import encrypt
@click.command()
@click.option(
'--input_file',
type=click.File('r'),
required=True,
)
@click.option(
'--output_file',
type=click.File('w'),
required=True,
)
def caesar_breaker(input_file, output_file):
cyphertext = input_file.read()
english_dictionnary = enchant.Dict("en_US")
best_number_of_english_words = 0
for key in tqdm(range(26)):
plaintext = encrypt(cyphertext, -key)
number_of_english_words = 0
for word in plaintext.split(' '):
if word and english_dictionnary.check(word):
number_of_english_words += 1
if number_of_english_words > best_number_of_english_words:
best_number_of_english_words = number_of_english_words
best_plaintext = plaintext
best_key = key
click.echo(f'The most likely encryption key is {best_key}. It gives the following plaintext:nn{best_plaintext[:1000]}...')
output_file.write(best_plaintext)
if __name__ == '__main__':
caesar_breaker()

这里使用了tqdm库,tqdm.tqdm类可以将任何可迭代对象转化为一个进度条。click也提供了类似的接口来创建进度条(click.progress_bar),但我觉得它不如tqdm好用。

Utilisez Click pour créer le programme de ligne de commande Python parfait

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer