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

    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' (скан произвольного списка портов)
      Это однозначно плюс − показывает, что вы умеете работать в команде и пользоваться системами контроля версий.

    Вроде бы наиболее серьёзные проблемы я перечислил. Выложу более полный вариант кода в комментариях.
    Ответ написан
    1 комментарий
  • Куда сохранять однотипный большой набор данных в Python?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Ответ написан
    Комментировать
  • Какое рабочее окружение лучше подобрать на python?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Начал всерьёз разрабатывать на Python лет 5-6 назад, и сразу в Linux. В то время многие Python-модули требовали компиляции и не запускались в Windows без установки платной и монструозной Visual Studio (причём конкретной версии, скорее всего, устаревшей), а также долгих и упорных плясок с бубном. Позже появился формат `wheel`, бесплатный набор CLI-утилит из VS, хорошо развился `mingw-w64`, и тогда биллибоям стало вроде как проще в пайтон. Но я так и остался на линуксе, что и другим рекомендую.

    Использую `virtualenv` и `pyenv`. Не использую `docker`, для разработчика это оверкилл. Пусть с ним девопс-инженеры любятся. Любимая IDE − `PyCharm`: в ней пишу, дебажу, гоняю тесты и командую гитом. В принципе, пайчармовский дебаггер имеет удалённый режим, которым можно дебажить прямо в контейнере, но пока что от этой фичи имхо больше гемора, чем пользы.
    Ответ написан
    1 комментарий
  • Многозадачный сервер на Python?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Я бы использовал Django channels.
    Ответ написан
    Комментировать
  • Как менять пути к файлам при сборке релиза?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Я не знаю насчёт bash, но вам, наверное, пора научиться делать Python-пакеты при помощи pip и setuptools. Вот хороший гайд, можете пропустить часть “Uploading the distribution archives” и далее, если не хотите делать ваши пакеты общедоступными. И не забывайте про pip install -e.
    Ответ написан
    Комментировать
  • Как это работает(python,super)?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    В данном случае это работает примерно как Car.__init__(self).

    В общем случае super() возвращет класс, ближайший к текущему согласно MRO. Вот вроде неплохая статья на русском: https://habr.com/post/62203/.
    Ответ написан
    Комментировать
  • Как в Python 3 вызвать функцию чьё название написано в строке?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Вообще, сама постановка вопроса кагбе намекает на архитектурные проблемы такого подхода. Но, если решать вопрос «в лоб», то вот самый логичный вариант:

    call_func = input("Введите название функции чтобы её вызвать: ")
    locals()[call_func]()


    Это я подразумеваю, что функции определены в том же неймспейсе, что и call_func. Если они определены в неймспейсе модуля, а call_func − глубже, в функции или методе, то поможет globals(). А если они вообще где-то в других модулях, которые могут не быть импортированы заранее, то нужно копать в сторону importlib.import_module, но это уж точно оверкилл.

    Ну и, конечно, использовать пользовательский ввод тут небезопасно. Не так опасно, как eval, но тоже нехорошо.
    Ответ написан
    Комментировать
  • Как принудительно заблокировать исполнение цикла while и всех циклов что внутри него?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Поместите внешний while в функцию; делайте return для останова всех циклов.

    Управление выполнением через исключение тоже допустимо в Python, но return лучше читается.
    Ответ написан
    Комментировать
  • Что делает фрагмент кода sum = arg[0]?(пояснение ниже)!?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Вот это место в официальных доках:

    https://docs.python.org/3/tutorial/controlflow.htm...

    Вот неплохая статья на эту тему:

    https://tproger.ru/translations/python-args-and-kwargs/
    Ответ написан
    Комментировать
  • Как реализовать парсер?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Используйте Scrapy, оно умеет в потоки.
    Ответ написан
    Комментировать
  • Что происходит в uWSGI с subprocess.Popen?

    Tanner
    @Tanner
    Огромный человекоподобный боевой робот
    Будучи потомственным телепатом, предположу, что вы в файле my_worker.py делаете импорты, которые приводят к запуску встроенного веб-сервера flask. Где-то не хватает идиоматического “if __name__ == '__main__':”.
    Ответ написан
    1 комментарий