@belikovss

Прошу помощи. В каком направлении мне двигаться?

Всем ку. Мне 16 лет, интересуюсь и увлекаюсь программированием. Очень понравился язык Python.
Написал программу, которая осуществляет проверку портов, выставляю ее на оценку.
Очень прекрасно понимаю, что она ужасная, но всё же хочу услышать мнение более опытных людей, в этом деле.
Перед ее использованием , необходимо установить модули:
termcolor
threading

import time
import sys
from termcolor import colored
import threading
import socket

# создание красивого меню (вверх)
def animate_menu_up():
	# объвяляю глобальную переменную в функции
	global flag
	# особождение строки
	print("\n")
	# переменная symbol будет содержать символ применяемый в анимации
	symbol = '~'
	# переменная string будет содержать результат анимации
	string = ""
	# анимация в цикле
	while len(string) <= 50:
		string += symbol
		sys.stdout.write(f"\r{string}")
		time.sleep(0.01)
	# освобождение места для следующей анимации
	print("", flush=True)
	flag = 1

# создание анимации центрального меню
def animate_menu_center():
	# объвяляю глобальную переменную в функции
	global flag
	# настройки для анимации
	string_first = list("[1] scan all ports")
	string_second = list("[2] scan enter port")
	string_three = list("SCANNER V1.0")
	# результат
	string_result_one = ""
	string_result_two = ""
	string_result_three = ""

	# анимация названия
	while len(string_result_three) != len(string_three):
		for char in string_three:
			string_result_three += char
			sys.stdout.write(f"\r\t\t{string_result_three}")
			time.sleep(0.04)
	# выделение места под другую строку
	print("\n")
	# сама анимация
	while len(string_result_one) != len(string_first):
		for char in string_first:
			string_result_one += char
			sys.stdout.write(f"\r\t{string_result_one}")
			time.sleep(0.04)
	# выделение места под другую строку
	print("")
	# анимация второй строки
	while len(string_result_two) != len(string_second):
		for char in string_second:
			string_result_two += char
			sys.stdout.write(f"\r\t{string_result_two}")
			time.sleep(0.04)
	# принудительно ощичаем поток
	sys.stdout.flush()
	print("", flush=True)
	flag = 2

# создание анимации меню (низ)
def animate_menu_down():
	# объвяляю глобальную переменную в функции
	global flag
	# тоже самое, что и в animate_menu_up
	symbol = '~'
	# тоже самое, что и в animate_menu_up
	string = ""
	# тоже самое, что и в animate_menu_up
	while len(string) <= 50:
		string += symbol
		sys.stdout.write(f"\r{string}")
		time.sleep(0.01)
	# принудительно очищаем поток
	sys.stdout.flush()
	print("")
	flag = 3

# функция, которая будет проверять выбранный режим
# и что-то делать
def select_mode():
	# переменная, которая будет содержать выбранный режим
	answer = input("\n[scan] Select mode: ")
	# переменная, которая будет содержать хост, для проверки портов
	hostname = input("[scan] Enter hostname: ")
	
	if answer == "1":
		# если режим проверки всех популярных портов
		# создается список с известными портами
		ports = [22, 80, 7777, 2516]

		print("")

		while True:
			for port in ports:
				# создаю объект сокета, который в будущем будет помогать проверять порты
				sock = socket.socket()
				try:
					sock.settimeout(0.5)
					sock.connect((hostname, port))
				except socket.error as socketerror:
					print(colored(f"[!] ", "red") + str(port) + " --CLOSE")
				else:
					print(colored(f"[#] ", "green") + str(port) + " --OPEN")
			break


# переменная, которая содержит выбранный режим
answer = ""
# переменная - флаг, нужна чтобы контролировать выполнение потоков
flag = 0
# создание объекта Thread
th_one = threading.Thread(target=animate_menu_up)
th_two = threading.Thread(target=animate_menu_down)
th_three = threading.Thread(target=animate_menu_center)

# запуск потоков
th_one.start()
while True:
	try:
		if flag == 1:
			th_three.start()
		elif flag == 2:
			th_two.start()
		elif flag == 3:
			select_mode()
	except RuntimeError:
		continue

	
#th_two.start()


ссылка на файл (на всякий) - https://disk.yandex.ru/d/Mq8d9johP45W0g
  • Вопрос задан
  • 263 просмотра
Решения вопроса 1
Tanner
@Tanner
Огромный человекоподобный боевой робот
Для начала, мне непонятно, почему все части вашей программы, которые вполне могут выполняться синхронно, работают в потоках. Есть часть программы, которая выиграла бы от многопоточности − сканирование портов, где логично было бы создать по потоку на каждый порт (если портов слишком много, то создать пул потоков, определить стратегию переиспользования потоков, в общем, тут большой простор для творчества), но у вас порты сканируются в одном потоке, последовательно.

Но давайте притворимся, что так и надо, и поговорим о других проблемах вашего кода.
  • Не стоит использовать глобальные переменные в программах длиннее одного экрана. Вместо global лучше объявить глобальный объект и спрятать flag в него:
    class PortScanner:
        
        def __init__(self):
            self.flag = 0
            
        def animate_menu_up(self):
            print("\n")
            ...
            self.flag = 1


  • Код инициализации программы тоже лучше перенести в __init__() глобального объекта. И основной цикл вынести в отдельный метод, например, run(). Тогда на нижнем уровне у нас останется что-то вроде:
    import ...
    
    class PortScanner:
        ...
    
    if __name__ == '__main__':
        main_obj = PortScanner()
        main_obj.run()

    этот идиоматичный код позволит импортировать класс PortScanner в другой скрипт, а также обеспечит плюсик на собеседовании/ревью,
  • приучайтесь использовать докстринги вместо комментариев:
    def animate_menu_up():
        """ Создание красивого меню (вверх). """

  • лишний цикл while True: в select_mode(),
  • слишком много магии. По мере роста программы становится всё тяжелее держать и сопоставлять в голове всякие абстрактные значения. Вот литералы, которые, по моему мнению, стоило бы определить как константы − или в «шапке» скрипта, или как атрибуты класса:
    MF_INITIAL = 0
    MF_MENU_CENTER = 1
    MF_MENU_DOWN = 2
    MF_SELECT = 3
    
    SCREEN_WIDTH = 50
    ALL_PORTS = [22, 80, 7777, 2516]
    SOCK_TIMEOUT = 0.5
    ANIM_SYMBOL = '~'
    ANIM_DELAY = 0.02

  • такие вещи очень больно стреляют в ногу и почти гарантированно проваливают собеседования:
    except RuntimeError:
        continue
    если вы действительно хотите продолжить выполнение программы после такой ошибки (что в обычных обстоятельствах бессмысленно и опасно), то позаботьтесь хотя бы о правильной индикации:
    import traceback
    ...
    except RuntimeError:
        traceback.print_exc(file=sys.stdout)
        continue

  • раз уж мы задержались здесь, давайте сделаем диспетчер более идиоматичным:
    while True:
        try:
            {
                MF_MENU_CENTER: th_three.start,
                MF_MENU_DOWN: th_two.start,
                MF_SELECT: select_mode,
            }[flag]()
        except RuntimeError:
            traceback.print_exc(file=sys.stdout)
            continue
    Такой наивный подход выводит много шелухи на экран, но это не важно. Важно то, что такой код проще читать и дорабатывать, чем цепочку if...elif...else.

  • Работа со строками тоже напрашивается на улучшения:
    1. нет смысла разбивать строки на списки, к символам в строке можно обращаться так же, как к элементам списка − по индексу и с помощью срезов,
    2. не нужно копировать строки,
    3. не нужно печатать всю строку с начала, это делает анимацию неровной − конец строки печатается медленнее, чем начало. Достаточно допечатать один символ в строке.
    Например:
    def animate_menu_center():
        """ Создание анимации центрального меню. """
        # настройки для анимации
        output_strings = [
            '[1] scan all ports',
            '[2] scan enter port',
            '[3] exit',
            'SCANNER V1.0',
        ]
    
        # анимация названия
        for output_string in output_strings:
            print('\r\t\t', end='')
            for ch in output_string:
                print(ch, end='')
                time.sleep(ANIM_DELAY)
            # last string?
            if output_string != output_strings[-1]:
                # new line
                print()
    
        print('', flush=True)
        flag = MF_MENU_DOWN
    (Я убрал цвета для простоты.)
  • юзабилити сильно страдает из-за отсутствия нормальной функции выхода из приложения,
  • не оставляйте закомментированный код в файле, который отдаёте на ревью, это минус. Вместо этого напишите комментарий с пометкой “TODO”, например:
    # TODO: реализовать режим '2' (скан произвольного списка портов)
    Это однозначно плюс − показывает, что вы умеете работать в команде и пользоваться системами контроля версий.

Вроде бы наиболее серьёзные проблемы я перечислил. Выложу более полный вариант кода в комментариях.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@mkone112
Начинающий питонист.
Написал программу, которая осуществляет проверку портов, выставляю ее на оценку.
Очень прекрасно понимаю, что она ужасная, но всё же хочу услышать мнение более опытных людей, в этом деле.

Да, она действительно ужасная. Обращайся.
Ответ написан
Комментировать
sergey-gornostaev
@sergey-gornostaev Куратор тега Python
Седой и строгий
Самое очевидное - избавьтесь от global. Этот оператор - всегда маркер плохого кода.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
22 нояб. 2024, в 03:54
1500 руб./за проект
22 нояб. 2024, в 02:56
10000 руб./за проект
22 нояб. 2024, в 00:55
500 руб./за проект