Вопрос или проблема
Я довольно новичок в Python и создал эту программу для вычисления хеша sha256 или sha512 для данного файла и преобразования его в шестнадцатеричный формат.
Она состоит из 5 файлов, 4 из которых – это пользовательские модули, а 1 – главный_.
У меня есть две функции в разных модулях, но единственное различие между этими функциями – это одна переменная. См. ниже:
Из файла sha256.py
def get_hash_sha256():
global sha256_hash
filename = input("Введите имя файла: ")
sha256_hash = hashlib.sha256()
with open(filename, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
# print("значение sha256: \n" + Color.GREEN + sha256_hash.hexdigest())
print(Color.DARKCYAN + "значение sha256 было рассчитано")
color_reset()
Из файла sha512.py
def get_hash_sha512():
global sha512_hash
filename = input("Введите имя файла: ")
sha512_hash = hashlib.sha512()
with open(filename, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha512_hash.update(byte_block)
# print("значение sha512: \n" + Color.GREEN + sha512_hash.hexdigest())
print(Color.DARKCYAN + "значение sha512 было рассчитано")
color_reset()
Эти функции вызываются в моем файле simple_sha_find.py:
def which_hash():
sha256_or_sha512 = input("Какой хеш вы хотите вычислить: sha256 или sha512? \n")
if sha256_or_sha512 == "sha256":
get_hash_sha256()
verify_checksum_sha256()
elif sha256_or_sha512 == "sha512":
get_hash_sha512()
verify_checksum_sha512()
else:
print("Введите либо sha256, либо sha512. Если вы введете что-то еще, программа закроется...вот так.")
sys.exit()
if __name__ == "__main__":
which_hash()
Как видите, функции, которые будут вызваны, основаны на вводе пользователя. Если пользователь вводит sha256, то вызываются функции из sha256.py, но если они вводят sha512, то вызываются функции из sha512.py.
Приложение работает, но я знаю, что могу сделать его менее избыточным, но не знаю как.
Как я могу определить функции get_hash_sha—() и verify_checksum_sha—() один раз, и они выполняли необходимые вычисления в зависимости от того, что выберет пользователь: sha256 или sha512?
Пожалуйста, дайте знать, если я что-то неясно объяснил, и спасибо заранее.
Я выполнил несколько вариантов кодирования этой программы.
Я создал его как один файл, а также создал разные модули и вызывал функции из этих модулей.
В любом случае у меня была повторяемость, но я знаю, что это порождает противоречие с целью автоматизации.
Возможно, потому что поздно, и моя концентрация почти исчерпана, но я даже не знаю, с чего начать.
Вы можете объединить эти 2 функции в одну:
import hashlib
def get_hash(hash_type):
if hash_type == 'sha256':
hash_obj = hashlib.sha256()
elif hash_type == 'sha512':
hash_obj = hashlib.sha512()
else:
print("Неверный тип хеша. Пожалуйста, выберите 'sha256' или 'sha512'")
return
filename = input("Введите имя файла: ")
try:
with open(filename, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
hash_obj.update(byte_block)
print(Color.DARKCYAN + f"значение {hash_type} было вычислено")
color_reset()
except FileNotFoundError:
print(f"Файл '{filename}' не найден.")
def which_hash():
sha_type = input("Какой хеш вы хотите вычислить: sha256 или sha512? \n").lower()
if sha_type in ['sha256', 'sha512']:
get_hash(sha_type)
verify_checksum(sha_type)
else:
print("Введите sha256 или sha512. Если вы введете что-то еще, программа закроется.")
sys.exit()
if __name__ == "__main__":
which_hash()
Также хорошей практикой является использование Enum вместо обычного текста:
from enum import Enum
class HashType(Enum):
SHA256 = 'sha256'
SHA512 = 'sha512'
Таким образом, вы можете изменить
if hash_type == HashType.SHA256:
hash_obj = hashlib.sha256()
elif hash_type == HashType.SHA512:
hash_obj = hashlib.sha512()
def which_hash():
sha_type_input = input("Какой хеш вы хотите вычислить: sha256 или sha512? \n").lower()
try:
sha_type = HashType(sha_type_input)
get_hash(sha_type)
verify_checksum(sha_type)
except ValueError:
print("Введите либо sha256, либо sha512. Если вы введете что-то еще, программа закроется.")
sys.exit()
Вы можете реорганизовать функции так, чтобы тип хеша был параметром. Возможно, также стоит избежать использования глобальных переменных и оставить любой интерактивный ввод/вывод кода вызывающему коду.
Я также изменил код, чтобы он вызывал ошибку, когда возникает проблема. Простая печать сообщения об ошибке подходит для очень простых программ, но повторно используемому коду нужно правильно различать успешные и неуспешные операции.
def get_hash(hash_type, filename):
if hash_type == 'sha256':
hash_obj = hashlib.sha256()
elif hash_type == 'sha512':
hash_obj = hashlib.sha512()
else:
raise ValueError("Неверный тип хеша. Пожалуйста, выберите 'sha256' или 'sha512'")
# Не перехватывайте ошибку
with open(filename, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
hash_obj.update(byte_block)
def which_hash():
sha_type = input("Какой хеш вы хотите вычислить: sha256 или sha512? \n").lower()
if sha_type in ['sha256', 'sha512']:
filename = input("Имя файла: ")
hash = get_hash(sha_type, filename)
print(f"{Color.DARKCYAN}{hash_type} значение было рассчитано")
color_reset()
verify_checksum(hash, sha_type, filename)
else:
raise ValueError("Введите sha256 или sha512")
Такой “случай и вызов” – это именно то, что объектно-ориентированное программирование было создано для избегания, но возможно, вам еще рано разбираться с этой темой.
Вы можете обобщить функцию, которая генерирует хеш, передавая соответствующую хеширующую функцию в качестве аргумента.
Что-то вроде этого:
import hashlib
from typing import Callable
def make_hash(filename: str, hash_function: Callable) -> str:
hash = hash_function()
with open(filename, "rb") as data:
while buffer := data.read(4096):
hash.update(buffer)
return hash.hexdigest()
def main():
filename = input("Введите имя файла: ")
func = input("Введите тип хеша (sha256 или sha512): ")
match func:
case "sha256":
print(make_hash(filename, hashlib.sha256))
case "sha512":
print(make_hash(filename, hashlib.sha512))
case _:
print("Неверный выбор типа хеша")
if __name__ == "__main__":
main()
Я не вижу никакой избыточности, которая затрудняет работу кода. Можете уточнить, где именно вы считаете, что избыточность мешает производительности вашего кода?
Ответ или решение
Безусловно, вы можете оптимизировать ваш код, исключив дублирование функций для вычисления хэша. Вместо создания отдельных функций для sha256
и sha512
, можно объединить их в одну универсальную функцию, которая будет принимать тип хэша в качестве аргумента. Я опишу, как это сделать.
Пример реализации
- Создайте одну функцию для вычисления хэша и передавайте в нее требуемый тип хэша как аргумент.
- Убедитесь, что не используется глобальная переменная для хранения хэша; вместо этого возвращайте значение из функции.
Вот как может выглядеть ваш код после изменений:
import hashlib
class Color:
DARKCYAN = "\033[36m"
GREEN = "\033[32m"
def color_reset():
print("\033[0m", end="")
def get_hash(hash_type, filename):
if hash_type == 'sha256':
hash_obj = hashlib.sha256()
elif hash_type == 'sha512':
hash_obj = hashlib.sha512()
else:
raise ValueError("Неверный тип хэша. Выберите 'sha256' или 'sha512'")
# Чтение файла и обновление хэша
try:
with open(filename, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
hash_obj.update(byte_block)
print(Color.DARKCYAN + f"{hash_type} значение было рассчитано.")
color_reset()
return hash_obj.hexdigest()
except FileNotFoundError:
print(f"Файл '{filename}' не найден.")
return None
def verify_checksum(hash_value, hash_type):
# Реализация функции проверки контрольной суммы (по вашему усмотрению)
print(f"Проверочная сумма {hash_type}: {hash_value}")
def which_hash():
sha_type = input("Какой хэш вы хотите вычислить: sha256 или sha512? \n").lower()
filename = input("Введите имя файла: ")
if sha_type in ['sha256', 'sha512']:
hash_value = get_hash(sha_type, filename)
if hash_value:
verify_checksum(hash_value, sha_type)
else:
print("Выберите либо sha256, либо sha512, программа закроется.")
sys.exit()
if __name__ == "__main__":
which_hash()
Преимущества данного подхода
- Сокращение дублирования кода: Теперь у вас есть только одна функция для вычисления хэша. Это упрощает поддержку и понимание кода.
- Отсутствие глобальных переменных: Код теперь более безопасен и чист, так как он не использует глобальные переменные, что уменьшает вероятность возникновения ошибок.
- Гибкость: Используя этот метод, вы можете легко добавлять поддержку новых алгоритмов хэширования в будущем, просто добавив дополнительные условия в функцию
get_hash
.
Альтернативные улучшения
Если вам нужны улучшения, вы можете рассмотреть использование enum
для определения типов хэширования, чтобы избежать возможных ошибок при вводе данных пользователем.
Заключение
Этот подход делает ваш код более чистым и легким для понимания. Оптимизация кода – это шаг на пути к написанию качественного программного обеспечения, и ваша инициатива в этом направлении заслуживает похвалы. Изучение таких концепций, как функции высшего порядка и использование enum
, поможет вам стать более уверенным программистом.