Mật mã Python 3 Vigenere
Tôi vẫn còn khá mới đối với Python và tôi đang cố gắng xem liệu tôi đã sử dụng hiệu quả các mô-đun, hàm, v.v. hay có cách khác / dễ dàng hơn để làm điều gì đó hay không.
Mật mã Python 3 Vigenere này là bản xây dựng lại mật mã dựa trên JavaScript và dựa trên Windows. Nó chấp nhận bất kỳ ký tự bảng chữ cái nào và có các tùy chọn demo được tích hợp sẵn. Bản demo mật mã sử dụng giai đoạn 1 của mật mã CIA Kryptos .
#! python
import os
import re
## Initialize global variables
continue_cipher = ""
demo_alphabet = "KRYPTOSABCDEFGHIJLMNQUVWXZ"
demo_key = "PALIMPSEST"
demo_cipher_string = "EMUFPHZLRFAXYUSDJKZLDKRNSHGNFIVJYQTQUXQBQVYUVLLTREVJYQTMKYRDMFD"
demo_cipher_decoded = "BETWEENSUBTLESHADINGANDTHEABSENCEOFLIGHTLIESTHENUANCEOFIQLUSION"
## Visuals
def display_header():
print("################################################")
print("# #")
print("# --- VIGENERE CIPHER --- #")
print("# #")
print("# A simple Vigenere cipher decoder/encoder #")
print("# #")
print("################################################", end="\n\n")
return
def display_results(mode, cipher_vars):
# Clear screen for final results
os.system('cls')
# Display header
display_header()
# Decompose cipher_vars
(alphabet, key, cipher_string, results) = cipher_vars
print("Mode:", "Decrypt" if mode == "D" else "Encrypt", end="\n\n")
print("Alphabet:", alphabet)
print("Key:", key)
print("Cipher String:", cipher_string, end="\n\n")
print("Decoded string:" if mode == "D" else "Encoded string:", results, end="\n\n")
return
## Validations
def string_is_alpha(input_string):
return True if re.match("^[a-zA-Z_]*$", input_string) else False
## Cipher variables
def get_alphabet():
global demo_alphabet
while True:
alphabet = input("Enter cipher alphabet: ").upper()
if alphabet == "":
alphabet = demo_alphabet
break
elif string_is_alpha(alphabet) is False:
print("The alphabet is not valid. Alphabet should not contain spaces, digits or special characters.")
else:
break
return alphabet
def get_key():
global demo_key
while True:
key = input("Enter cipher key: ").upper()
if key == "":
key = demo_key
break
elif string_is_alpha(key) is False:
print("The key is not valid. Key should not contain spaces, digits or special characters.")
else:
break
return key
def get_cipher_string(mode):
global demo_cipher_string
global demo_cipher_decoded
while True:
cipher_string = input("Enter cipher string: ").upper()
if cipher_string == "":
cipher_string = demo_cipher_string if mode == "D" else demo_cipher_decoded
break
elif string_is_alpha(cipher_string) is False:
print("The cipher string is not valid. Cipher strings should not contain spaces, digits or special characters.")
else:
break
return cipher_string
## Cipher actions
def get_cipher_alphabets(alphabet, key):
cipher_alphabets = []
for char in key:
char_index = alphabet.find(char)
cipher_alphabet = alphabet[char_index:] + alphabet[:char_index]
cipher_alphabets.append(cipher_alphabet)
return cipher_alphabets
def start_cipher(mode, alphabet, key, cipher_string):
mode_string = ""
cipher_alphabets = get_cipher_alphabets(alphabet, key)
cipher_alphabet_index = 0
for char in cipher_string:
# Reset cipher_alphabet_index to 0 when at end of cipher alphabets
if cipher_alphabet_index == len(cipher_alphabets):
cipher_alphabet_index = 0
# Use appropriate alphabet based on mode
# Syntax: base_alphabet[mode_alphabet.find(char)]
if mode == "D":
mode_string += alphabet[cipher_alphabets[cipher_alphabet_index].find(char)]
else:
mode_string += cipher_alphabets[cipher_alphabet_index][alphabet.find(char)]
cipher_alphabet_index += 1
return mode_string
## Cipher Mode
def get_cipher_mode():
while True:
cipher_mode = input("Choose cipher mode - [D]ecrypt or [E]ncrypt: ").upper()
if cipher_mode != "D" and cipher_mode != "E":
print("That is not a valid option. Please enter 'D' for decrypt and 'E' for encrypt.")
else:
break
print("")
return cipher_mode
def start_cipher_mode(mode):
print("Press 'enter' to use demo options")
alphabet = get_alphabet()
key = get_key()
cipher_string = get_cipher_string(mode)
mode_string = start_cipher(mode, alphabet, key, cipher_string)
return alphabet, key, cipher_string, mode_string
## Loop cipher
def get_continue_cipher():
while True:
continue_cipher = input("Do you want to decode/encode more? [Y/N]: ").upper()
if continue_cipher != "Y" and continue_cipher != "N":
print("That is not a valid option. Please enter 'Y' to continue and 'N' to quit.")
else:
break
return continue_cipher
## Start vigenere cipher program
while continue_cipher != "N":
# Clear the screen after each operation
os.system('cls')
# Display header
display_header()
# Determine cipher mode
cipher_mode = get_cipher_mode()
cipher_vars = start_cipher_mode(cipher_mode)
# Display results
display_results(cipher_mode, cipher_vars)
continue_cipher = get_continue_cipher()
Trả lời
shebang
Shebang nên chung chung. Bạn hiện đang gọi python, điều này có thể trỏ đến python 2 trên một số hệ thống nhất định.
Một shebang python chung, thân thiện với môi trường ảo là:
#!/usr/bin/env python3
PEP-8
Vài điểm từ hướng dẫn PEP-8 :
- Bao quanh định nghĩa lớp và chức năng cấp cao nhất bằng hai dòng trống.
- Sử dụng các dòng trống trong các hàm, một cách tiết kiệm, để chỉ ra các phần hợp lý.
- Hằng số thường được định nghĩa ở cấp độ mô-đun và được viết bằng tất cả các chữ cái in hoa với dấu gạch dưới ngăn cách các từ.
PEP-484
Nhập các chức năng gợi ý giúp bạn dễ dàng theo dõi các chức năng của mình hơn. Kiểm tra PEP-484 .
if __name__ khối
Đặt logic thực thi của tập lệnh của bạn bên trong if __name__ == "__main__"khối. Có thể kiểm tra phần giải thích mô tả hơn trên Stack Overflow .
Logic dư thừa
Trong mã của bạn, bạn có 5 chức năng khác nhau, chỉ để đọc đầu vào của người dùng. Tất cả chúng đều có chung một công việc là:
- vòng lặp vô hạn
- yêu cầu người dùng nhập
- chuyển sang chữ hoa
- xác thực nếu đầu vào trống (hoặc trong một tập hợp các giá trị hợp lệ)
- trong trường hợp giá trị trống, hãy trả về giá trị mặc định
- trở lại với giá trị
Tất cả điều này có thể được xử lý bởi một chức năng duy nhất:
def ask_user_input(message: str, options: List[str] = None, default: str = None, check_alpha: bool = False) -> str:
if not any([options, default]):
raise ValueError("Either a set of `options` for validation or a fallback `default` needed.")
while True:
value = input(message).upper()
if options:
if value in options:
break
else:
print(f"Invalid value. Select one of {', '.join(options)}")
continue
if default is not None:
if not value:
value = default
break
elif not check_alpha:
break
elif not (value.isalpha() and value.isascii()):
print("The input text should only consist of ascii alphabets.")
continue
else:
break
return value
Regex / xác thực
Regex để xác thực đầu vào cho phép _, trong khi thông báo lỗi cho biết không có ký tự đặc biệt. Kiểm tra trong phần viết lại ở trên được thực hiện bằng cách sử dụng (cập nhật dựa trên nhận xét bên dưới):
value.isalpha() and value.isascii()
sẽ hoạt động nhanh hơn regex (trừ khi người dùng tiếp tục nhập các giá trị sai \$ 10^ n \$thời gian mà mẫu biên dịch trước có thể hoạt động tốt hơn một chút).
Hiệu suất
Một số điều có thể được thay đổi để làm cho mã hoạt động hiệu quả hơn:
Thay vì nối (nối) vào chuỗi
mode_string, hãy đẩy đến một danh sách và ở cuối, hãy sử dụng"".join(). Thêm chi tiết về Stack Overflow .Bạn cũng có thể làm cho chương trình của mình hỗ trợ hệ thống linux (* nix). Sự phụ thuộc duy nhất vào windows là lệnh gọi hệ thống của bạn tới
cls. Có lẽ ( lấy từ Stack Overflow ):def clear(): os.system("cls" if os.name == "nt" else "clear")Có 2 hàm có tên rất giống nhau:
start_cipher(mode...)vàstart_cipher_mode(mode). Điều này làm cho nó thực sự khó khăn để biết cái nào đang thực sự bắt đầu mật mã . Có lẽ, có 2 chức năng riêng biệtencryptvàdecrypt?Sử dụng thao tác modulo, bạn có thể loại bỏ điều kiện sau:
if cipher_alphabet_index == len(cipher_alphabets): cipher_alphabet_index = 0và trông giống như:
result.append(alphabet[cipher_alphabets[cipher_alphabet_index % alphabets_length].find(char)]Vì bạn chỉ sử dụng
alphabetchuỗi để thực sự làm việc với các giá trị chỉ mục của các ký tự trong đó, hãy tạo một từ điển. Tra cứu trong từ điển là \$ O(1) \$so với \$ O(n) \$cho.find(). Điều này sẽ:from itertools import count alphabet_map = dict(zip(alphabet, count()))Từ 2 điểm trên, rõ ràng là bạn không thực sự cần các ký tự / bảng chữ cái sau khi người dùng nhập. Chỉ mô-đun giá trị chỉ mục mới quan trọng. Điều này có thể khó hiểu / khó thực hiện nếu không có hiểu biết toán học đáng kể, vì vậy bạn có thể bỏ qua điều này ngay bây giờ.
@hjpotter đã bao gồm hầu hết các nhận xét của tôi.
Booleans
Python có khái niệm về giá trị true và falsy , vì vậy chỉ nên coi các giá trị là boolean trực tiếp hơn là so sánh chúng với True hoặc False.
return True if re.match("^[a-zA-Z_]*$", input_string) else False
có thể được đơn giản hóa thành:
return re.match("^[a-zA-Z_]*$", input_string)
elif string_is_alpha(alphabet) is False:
Điều này có thể được đơn giản hóa thành:
elif not string_is_alpha(alphabet):
Nếu chung chung, bạn hiếm khi muốn sử dụng "is" để so sánh. (Ngoại lệ chính là so sánh với None.)
Biên dịch các biểu thức chính quy
Đây gần như chắc chắn là một cải tiến hiệu suất không cần thiết, nhưng có thể hữu ích để biết về sau:
Lệnh gọi re.matchcần biên dịch regexp mỗi khi nó được gọi. Bạn có thể biên dịch trước regexp một lần, sau đó gọi matchđối tượng đã biên dịch để tăng tốc nó.
Globals
Hầu như mỗi khi tôi tiếp cận với globaltừ khóa, nó đều bị nhầm lẫn.
Tôi không nghĩ rằng bạn cần phải khai báo số nhận dạng demo là toàn cục; chúng nên đã có sẵn để sử dụng (chỉ để đọc - nếu bạn cố gắng ghi vào chúng, bạn sẽ xác định một biến mới trong phạm vi mới, ẩn các biến gốc).