Maison >développement back-end >Tutoriel Python >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 :
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
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 ?
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.
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.
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()
示例中的文本包含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好用。
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!