Heim  >  Artikel  >  Backend-Entwicklung  >  Erstellen Sie mit Click das perfekte Python-Befehlszeilenprogramm

Erstellen Sie mit Click das perfekte Python-Befehlszeilenprogramm

WBOY
WBOYnach vorne
2023-04-18 14:55:031235Durchsuche

Erstellen Sie mit Click das perfekte Python-Befehlszeilenprogramm


Die Hauptaufgabe von Python-Programmierern besteht darin, Befehlszeilenprogramme zu schreiben, also Skripte, die direkt im Terminal ausgeführt werden. Mit zunehmender Größe des Projekts hoffen wir, eine effektive Befehlszeilenschnittstelle zu schaffen, die verschiedene Probleme lösen kann, indem sie unterschiedliche Parameter bereitstellt, anstatt den Quellcode jedes Mal zu ändern.

Um dieses Ziel zu erreichen, habe ich vier Prinzipien zusammengefasst, von denen ich hoffe, dass sie für alle hilfreich sind:

  • Befehlszeilenparameter sollten Standardwerte bereitstellen.
  • Behandeln Sie alle möglichen Parameterfehler, einschließlich fehlender Parameter und falscher Daten Typ und kann nicht gefunden werden. Dokumente usw.
  • Schreiben Sie eine gute Dokumentation, die erklärt, was die Parameter bedeuten und wie man sie einstellt.
  • Verwenden Sie einen Fortschrittsbalken, um lang laufende Aufgaben anzuzeigen.

Ein einfaches Beispiel.

Wenden wir diese Regeln an zu einem konkreten Fall: ein Caesar-Verschlüsselungsskript zum Verschlüsseln und Entschlüsseln von Nachrichten.

Angenommen, wir schreiben eine Verschlüsselungsfunktion wie unten gezeigt. Jetzt erstellen wir ein Skript zum Verschlüsseln und Entschlüsseln von Nachrichten.

Das Skript ermöglicht dem Benutzer die Auswahl von: Modus (Verschlüsselung oder Entschlüsselung), Schlüssel. Der Standardwert für Ersteres ist Verschlüsselung, und der Standardwert für Letzteres ist 1. Dies alles wird über Befehlszeilenparameter erreicht.

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

Anfängermethode: sys.argv

Das Skript muss zuerst den Wert des Befehlszeilenparameters abrufen. Verwenden wir zunächst die einfachste sys.argv, um ihn zu implementieren.

sys.argv ist eine Liste, die alle Parameter enthält, die der Benutzer beim Ausführen des Skripts eingegeben hat (einschließlich des Skriptnamens selbst).

Geben Sie den folgenden Befehl im Terminal ein:

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

sys.argv-Liste enthält:

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

Um den Parameterwert zu erhalten, müssen Sie die Parameterliste durchlaufen und nach einem „--key“ suchen. (oder „-k“), um den Schlüsselwert abzurufen und nach einem „--decrypt“-Abrufmodus zu suchen.

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

Der Code folgt den Prinzipien, die wir zu Beginn dargelegt haben:

Verfügt über einen Standardschlüsselwert und einen Standardmodus

Behandelt grundlegende Fehler (kein Eingabetext bereitgestellt oder unbekannte Parameter)

Bei Parameterfehlern oder ohne Parameter Geben Sie beim Aufrufen des Skripts eine kurze Eingabeaufforderungsnachricht aus

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

Aber diese Version des Skripts ist ziemlich lang (39 Zeilen, ohne Verschlüsselungsfunktion) und der Code ist sehr hässlich.

Gibt es eine bessere Möglichkeit, Befehlszeilenargumente zu analysieren?

Geben Sie argparse ein

argparse ist ein Python-Standardbibliotheksmodul zum Parsen von Befehlszeilenargumenten.

Ändern Sie das Skript, um argparse zum Parsen von Befehlszeilenargumenten zu verwenden:

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

Der Code entspricht weiterhin den von uns vorgeschlagenen Prinzipien und bietet eine präzisere Dokumentation und interaktivere Fehlerbehandlung als das manuelle Parsen von Befehlszeilenargumenten.

> 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

Die Zeilen 7 bis 13 des Skripts definieren Befehlszeilenargumente, aber sie sind nicht sehr elegant: Zu ausführlich und prozedural, wir können es kompakter und deklarativer machen.

Verwenden Sie Click, um eine bessere Befehlszeilenschnittstelle zu erstellen

Glücklicherweise gibt es eine Drittanbieter-Bibliothek zum Erstellen von Befehlszeilenschnittstellen. Sie bietet nicht nur mehr Funktionen als argparse, sondern verfügt auch über einen schöneren Codestil. Ersetzen Sie argparse durch click und optimieren Sie das Skript weiter.

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

Beachten Sie, dass Befehlszeilenargumente und -optionen in Dekoratoren deklariert werden, wodurch sie direkt als Argumente für Funktionen zugänglich sind.

Lassen Sie uns den obigen Code sorgfältig analysieren:

nargs definiert die Anzahl der vom Befehlszeilenargument empfangenen Werte. Der Standardwert ist 1, nargs=-1 ermöglicht die Bereitstellung einer beliebigen Anzahl von Wörtern.

--encrypt/--decrypt definiert sich gegenseitig ausschließende Optionen, die letztendlich als boolescher Wert an das Programm übergeben werden.

click.echo ist die von der Click-Bibliothek bereitgestellte Grundfunktion. Ihre Funktion ähnelt der von print, bietet jedoch leistungsfähigere Funktionen, wie z. B. das Anpassen der Farbe des auf der Konsole gedruckten Textes.

Eingabe aus lokaler Datei lesen

Der vom Befehlszeilenargument empfangene Wert ist eine streng geheime Nachricht, die verschlüsselt wird. Daher kann es zu Sicherheitsbedenken kommen, wenn der Benutzer aufgefordert wird, Klartext direkt in das Terminal einzugeben.

Eine sicherere Möglichkeit besteht darin, versteckte Hinweise zu verwenden oder den Text aus einer lokalen Datei zu lesen, was bei langen Texten praktischer ist.

Die gleiche Idee gilt für die Ausgabe: Der Benutzer kann sie in einer Datei speichern oder im Terminal ausdrucken. Lassen Sie uns das Skript weiter optimieren.

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

Da das Skript komplexer wird, erstellen wir ein Parameterdokument (implementiert durch Definieren des Hilfeparameters des click.option-Dekorators), um die Funktion des Parameters im Detail zu erläutern. Der Effekt ist wie folgt.

> 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.

Wir haben zwei neue Parameter input_file und output_file, der Typ ist click.File, click öffnet die Datei im richtigen Modus und behandelt mögliche Fehler. Beispielsweise kann die Datei nicht gefunden werden:

> 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

Wenn input_file nicht angegeben ist, verwenden wir click.prompt, um ein Eingabeaufforderungsfenster in der Befehlszeile zu erstellen, damit der Benutzer Text direkt eingeben kann. Die Eingabeaufforderung wird zur Verschlüsselung ausgeblendet Modus. Der Effekt ist wie folgt:

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

Erstellen Sie mit Click das perfekte Python-Befehlszeilenprogramm

使用进度条

示例中的文本包含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好用。

Erstellen Sie mit Click das perfekte Python-Befehlszeilenprogramm

Das obige ist der detaillierte Inhalt vonErstellen Sie mit Click das perfekte Python-Befehlszeilenprogramm. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:51cto.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen