Python src что это
Перейти к содержимому

Python src что это

  • автор:

src layout vs flat layout¶

The “flat layout” refers to organising a project’s files in a folder or repository, such that the various configuration files and import packages are all in the top-level directory.

. ├── README.md ├── noxfile.py ├── pyproject.toml ├── setup.py ├── awesome_package/ │ ├── __init__.py │ └── module.py └── tools/ ├── generate_awesomeness.py └── decrease_world_suck.py

The “src layout” deviates from the flat layout by moving the code that is intended to be importable (i.e. import awesome_package , also known as import packages ) into a subdirectory. This subdirectory is typically named src/ , hence “src layout”.

. ├── README.md ├── noxfile.py ├── pyproject.toml ├── setup.py ├── src/ │ └── awesome_package/ │ ├── __init__.py │ └── module.py └── tools/ ├── generate_awesomeness.py └── decrease_world_suck.py

Here’s a breakdown of the important behaviour differences between the src layout and the flat layout:

  • The src layout requires installation of the project to be able to run its code, and the flat layout does not. This means that the src layout involves an additional step in the development workflow of a project (typically, an editable installation is used for development and a regular installation is used for testing).
  • The src layout helps prevent accidental usage of the in-development copy of the code. This is relevant since the Python interpreter includes the current working directory as the first item on the import path. This means that if an import package exists in the current working directory with the same name as an installed import package, the variant from the current working directory will be used. This can lead to subtle misconfiguration of the project’s packaging tooling, which could result in files not being included in a distribution. The src layout helps avoid this by keeping import packages in a directory separate from the root directory of the project, ensuring that the installed copy is used.
  • The src layout helps enforce that an editable installation is only able to import files that were meant to be importable. This is especially relevant when the editable installation is implemented using a path configuration file that adds the directory to the import path. The flat layout would add the other project files (eg: README.md , tox.ini ) and packaging/tooling configuration files (eg: setup.py , noxfile.py ) on the import path. This would make certain imports work in editable installations but not regular installations.

Table of Contents

  • An Overview of Packaging for Python
  • The Packaging Flow
  • Tutorials
  • Guides
  • Discussions
    • Deploying Python applications
    • pip vs easy_install
    • install_requires vs requirements files
    • Wheel vs Egg
    • src layout vs flat layout

    Как организовать код в Python-проекте, чтобы потом не пожалеть

    Python отличается от таких языков программирования, как C# или Java, заставляющих программиста давать классам имена, соответствующие именам файлов, в которых находится код этих классов.

    Python — это самый гибкий язык программирования из тех, с которыми мне приходилось сталкиваться. А когда имеешь дело с чем-то «слишком гибким» — возрастает вероятность принятия неправильных решений.

    • Хотите держать все классы проекта в единственном файле main.py ? Да, это возможно.
    • Надо читать переменную окружения? Берите и читайте там, где это нужно.
    • Требуется модифицировать поведение функции? Почему бы не прибегнуть к декоратору!?

    Применение многих идей, которые легко реализовать, может привести к негативным последствиям, к появлению кода, который очень тяжело поддерживать.

    Но, если вы точно знаете о том, что делаете, последствия гибкости Python не обязательно окажутся плохими.

    Здесь я собираюсь представить вашему вниманию рекомендации по организации Python-кода, которые сослужили мне хорошую службу, когда я работал в разных компаниях и взаимодействовал со многими людьми.

    Структура Python-проекта

    Сначала обратим внимание на структуру директорий проекта, на именование файлов и организацию модулей.

    Рекомендую держать все файлы модулей в директории src , а тесты — в поддиректории tests этой директории:

     ├── src │ ├── /* │ │ ├── __init__.py │ │ └── many_files.py │ │ │ └── tests/* │ └── many_tests.py │ ├── .gitignore ├── pyproject.toml └── README.md

    Здесь — это главный модуль проекта. Если вы не знаете точно — какой именно модуль у вас главный — подумайте о том, что пользователи проекта будут устанавливать командой pip install , и о том, как, по вашему мнению, должна выглядеть команда import для вашего модуля.

    Часто имя главного модуля совпадает с именем всего проекта. Но это — не некое жёсткое правило.

    Аргументы в пользу директории src

    Я видел множество проектов, устроенных по-другому.

    Например, в проекте может отсутствовать директория src , а все модули будут просто лежать в его корневой директории:

    non_recommended_project ├── /* │ ├── __init__.py │ └── many_files.py │ ├── .gitignore │ ├── tests/* │ └── many_tests.py │ ├── pyproject.toml │ ├── /* │ ├── __init__.py │ └── many_files.py │ └── README.md

    Уныло смотрится проект, в структуре которого нет никакого порядка из-за того, что его папки и файлы просто расположены по алфавиту, в соответствии с правилами сортировки объектов в IDE.

    Главная причина, по которой рекомендуется пользоваться папкой src , заключается в том, чтобы активный код проекта был бы собран в одной директории, а настройки, параметры CI/CD, метаданные проекта находились бы за пределами этой директории.

    Единственный минус такого подхода заключается в том, что, без дополнительных усилий, не получится воспользоваться в своём коде командой вида import module_a . Для этого потребуется кое-что сделать. Ниже мы поговорим о том, как решить эту проблему.

    Именование файлов

    Правило №1: тут нет файлов

    Во-первых — в Python нет таких сущностей, как «файлы», и я заметил, что это — главный источник путаницы для новичков.

    Если вы находитесь в директории, содержащей файл __init__.py , то это — директория, включающая в себя модули, а не файлы.

    Рассматривайте каждый модуль, как пространство имён.

    Я говорю о «пространстве имён», так как нельзя сказать с уверенностью — имеется ли в модуле множество функций и классов, или только константы. В нём может присутствовать практически всё что угодно, или лишь несколько сущностей пары видов.

    Правило №2: если нужно — держите сущности в одном месте

    Совершенно нормально, когда в одном модуле имеется несколько классов. Так и стоит организовывать код (но, конечно, только если классы связаны с модулем).

    Выделяйте классы в отдельные модули только в том случае, если модуль становится слишком большим, или если его разные части направлены на решение различных задач.

    Часто встречается мнение, что это — пример неудачного приёма работы. Те, кто так считают, находятся под влиянием опыта, полученного после использования других языков программирования, которые принуждают к другим решениям (например — это Java и C#).

    Правило №3: давайте модулям имена, представляющие собой существительные во множественном числе

    Давая модулям имена, следуйте общему правилу, в соответствии с которым эти имена должны представлять собой существительные во множественном числе. При этом они должны отражать особенности предметной области проекта.

    Правда, у этого правила есть и исключение. Модули могут называться core , main.py или похожим образом, что указывает на то, что они представляют собой некую единичную сущность. Подбирая имена модулей, руководствуйтесь здравым смыслом, а если сомневаетесь — придерживайтесь вышеприведённого правила.

    Реальный пример именования модулей

    Вот мой проект — Google Maps Crawler, созданный в качестве примера.

    Этот проект направлен на сбор данных из Google Maps с использованием Selenium и на их представление в виде, удобном для дальнейшей обработки (тут, если интересно, можно об этом почитать).

    Вот текущее состояние дерева проекта (тут выделены исключения из правила №3):

    gmaps_crawler ├── src │ └── gmaps_crawler │ ├── __init__.py │ ├── config.py (форма единственного числа) │ ├── drivers.py │ ├── entities.py │ ├── exceptions.py │ ├── facades.py │ ├── main.py (форма единственного числа) │ └── storages.py │ ├── .gitignore ├── pyproject.toml └── README.md

    Весьма естественным кажется такой импорт классов и функций:

    from gmaps_crawler.storages import get_storage from gmaps_crawler.entities import Place from gmaps_crawler.exceptions import CantEmitPlace

    Можно понять, что в exceptions может иметься как один, так и множество классов исключений.

    Именование модулей существительными множественного числа отличается следующими приятными особенностями:

    • Модули не слишком «малы» (в том смысле, что предполагается, что один модуль может включать в себя несколько классов).
    • Их, если нужно, в любой момент можно разбить на более мелкие модули.
    • «Множественные» имена дают программисту сильное ощущение того, что он знает о том, что может быть внутри соответствующих модулей.

    Именование классов, функций и переменных

    Некоторые программисты считают, что давать сущностям имена — это непросто. Но если заранее определиться с правилами именования, эта задача становится уже не такой сложной.

    Имена функций и методов должны быть глаголами

    Функции и методы представляют собой действия, или нечто, выполняющее действия.

    Функция или метод — это не просто нечто «существующее». Это — нечто «действующее».

    Действия чётко определяются глаголами.

    Вот — несколько удачных примеров из реального проекта, над которым я раньше работал:

    def get_orders(): . def acknowledge_event(): . def get_delivery_information(): . def publish(): . 

    А вот — несколько неудачных примеров:

    def email_send(): . def api_call(): . def specific_stuff(): . 

    Тут не очень ясно — возвращают ли функции объект, позволяющий выполнить обращение к API, или они сами выполняют какие-то действия, например — отправку письма.

    Я могу представить себе такой сценарий использования функции с неудачным именем:

    email_send.title = "title" email_send.dispatch()

    У рассмотренного правила есть и некоторые исключения:

    • Создание функции main() , которую вызовут в главной точке входа приложения — это хороший повод нарушить это правило.
    • Использование @property для того, чтобы обращаться с методом класса как с атрибутом, тоже допустимо.
    Имена переменных и констант должны быть существительными

    Имена переменных и констант всегда должны быть существительными и никогда — глаголами (это позволяет чётко отделить их от функций).

    Вот примеры удачных имён:

    plane = Plane() customer_id = 5 KEY_COMPARISON = "abc"

    Вот — неудачные имена:

    fly = Plane() get_customer_id = 5 COMPARE_KEY = "abc"

    А если переменная или константа представляют собой список или коллекцию — им подойдёт имя, представленное существительным во множественном числе:

    planes: list[Plane] = [Plane()] # Даже если содержит всего один элемент customer_ids: set[int] = KEY_MAP: dict[str, str] = # Имена словарей остаются существительными в единственном числе
    Имена классов должны говорить сами за себя, но использование суффиксов — это нормально

    Отдавайте предпочтение именам классов, понятным без дополнительных пояснений. При этом можно использовать и суффиксы, вроде Service , Strategy , Middleware , но — только в крайнем случае, когда они необходимы для чёткого описания цели существования класса.

    Всегда давайте классам имена в единственном, а не во множественном числе. Имена во множественном числе напоминают имена коллекций элементов (например — если я вижу имя orders , то я полагаю, что это — список или итерируемый объект). Поэтому, выбирая имя класса, напоминайте себе, что после создания экземпляра класса в нашем распоряжении оказывается единственный объект.

    Классы представляют собой некие сущности

    Классы, представляющие нечто из бизнес-среды, должны называться в соответствии с названиями связанных с ними сущностей (и имена должны быть существительными!). Например — Order , Sale , Store , Restaurant и так далее.

    Пример использования суффиксов

    Представим, что надо создать класс, ответственный за отправку электронных писем. Если назвать его просто Email , цель его существования будет неясна.

    Кто-то может решить, что он может олицетворять некую сущность:

    email = Email() # Предполагаемый пример использования email.title = "Title" email.body = create_body() email.send_to = "guilatrova.dev" send_email(email)

    Такой класс следует назвать EmailSender или EmailService .

    Соглашения по именованию сущностей

    Следуйте этим соглашениям по именованию сущностей:

    Тип

    Общедоступный

    Внутренний

    Структура проекта на Python: лучшие практики для новичков

    При разработке любого приложения, будь то веб-проект или приложение для рабочего стола, одним из важных аспектов является правильное структурирование проекта. Это обеспечивает удобство поддержки, дружественность к IDE, пригодность для ветвления и слияния исходного кода, а также упрощает создание установочных пакетов.

    Пример типичной структуры проекта

    /projectfolder /src /scripts /ide /tests /config /extensions

    Описание элементов структуры

    1. /src

    В этой папке обычно хранятся все исходные файлы Python. Это место для всех файлов .py , которые вы напишете для своего проекта.

    2. /scripts

    Здесь размещаются скрипты запуска приложения. Зачастую это файлы, которые запускают основную функцию проекта или служат точкой входа в приложение.

    3. /ide

    Эта папка предназначена для хранения файлов IDE, которые не относятся к исходному коду проекта. К таким файлам относятся, например, конфигурации IDE, специфические настройки и так далее.

    4. /tests

    В данном каталоге хранятся все тесты для проекта, включая модульные и приемочные тесты. Хорошей практикой считается создание отдельного файла тестов для каждого модуля исходного кода.

    5. /config

    Папка для хранения файлов конфигурации. Здесь могут находиться различные конфигурационные файлы, например, файлы .ini , .yaml или .json .

    6. /extensions

    В этом месте хранятся все не-Python исходные файлы, такие как C++ для бинарных расширений pyd/so.

    Заключение

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

    Что это за модуль src_rc?

    После создания виджета в Qt Designer и переноса в Python с помощью pyuic5 в самом конце кода образовался непонятный импорт: import src_rc .
    Вопрос: откуда он взялся и зачем он нужен. Если попробовать его установить pip его не находит.
    Вот код:

    from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1246, 698) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) MainWindow.setSizePolicy(sizePolicy) MainWindow.setMinimumSize(QtCore.QSize(1246, 698)) MainWindow.setMaximumSize(QtCore.QSize(1246, 698)) MainWindow.setStyleSheet("background: #191919;") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.nav_menu = QtWidgets.QGroupBox(self.centralwidget) self.nav_menu.setEnabled(True) self.nav_menu.setGeometry(QtCore.QRect(0, 0, 269, 698)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.nav_menu.sizePolicy().hasHeightForWidth()) self.nav_menu.setSizePolicy(sizePolicy) self.nav_menu.setMinimumSize(QtCore.QSize(269, 0)) self.nav_menu.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.nav_menu.setStyleSheet("border: 0;\n" "background: #151515;") self.nav_menu.setObjectName("nav_menu") self.btn_add_folder = QtWidgets.QLabel(self.nav_menu) self.btn_add_folder.setGeometry(QtCore.QRect(218, 30, 24, 31)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.btn_add_folder.sizePolicy().hasHeightForWidth()) self.btn_add_folder.setSizePolicy(sizePolicy) self.btn_add_folder.setMinimumSize(QtCore.QSize(24, 31)) self.btn_add_folder.setMaximumSize(QtCore.QSize(24, 31)) self.btn_add_folder.setText("") self.btn_add_folder.setPixmap(QtGui.QPixmap("F:/Projects/Python/.pyqt/Tasker/src/img/add_folder.svg")) self.btn_add_folder.setObjectName("btn_add_folder") self.logo = QtWidgets.QLabel(self.nav_menu) self.logo.setGeometry(QtCore.QRect(20, 20, 142, 52)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.logo.sizePolicy().hasHeightForWidth()) self.logo.setSizePolicy(sizePolicy) self.logo.setMaximumSize(QtCore.QSize(142, 52)) self.logo.setText("") self.logo.setPixmap(QtGui.QPixmap(":/src/img/logo.svg")) self.logo.setObjectName("logo") self.buttons = QtWidgets.QGroupBox(self.nav_menu) self.buttons.setGeometry(QtCore.QRect(23, 93, 219, 583)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.buttons.sizePolicy().hasHeightForWidth()) self.buttons.setSizePolicy(sizePolicy) self.buttons.setMaximumSize(QtCore.QSize(219, 583)) self.buttons.setTitle("") self.buttons.setObjectName("buttons") self.btn_menu1 = QtWidgets.QPushButton(self.buttons) self.btn_menu1.setGeometry(QtCore.QRect(0, 0, 219, 37)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.btn_menu1.sizePolicy().hasHeightForWidth()) self.btn_menu1.setSizePolicy(sizePolicy) self.btn_menu1.setMaximumSize(QtCore.QSize(219, 37)) font = QtGui.QFont() font.setFamily("Inter") font.setPointSize(-1) font.setBold(False) font.setItalic(False) font.setWeight(50) self.btn_menu1.setFont(font) self.btn_menu1.setStyleSheet("background: #1490AA;\n" "border-radius: 11px;\n" "font-family: \'Inter\';\n" "font-style: normal;\n" "font-weight: 400;\n" "font-size: 16px;\n" "line-height: 19px;\n" "text-align: left;\n" "color: #FFFFFF;\n" "padding: 8px 0 8px 15px;") self.btn_menu1.setObjectName("btn_menu1") self.btn_menu2 = QtWidgets.QPushButton(self.buttons) self.btn_menu2.setGeometry(QtCore.QRect(0, 47, 219, 37)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.btn_menu2.sizePolicy().hasHeightForWidth()) self.btn_menu2.setSizePolicy(sizePolicy) self.btn_menu2.setMaximumSize(QtCore.QSize(219, 37)) self.btn_menu2.setStyleSheet("background: #282828;\n" "border-radius: 11px;\n" "font-family: \'Inter\';\n" "font-style: normal;\n" "font-weight: 400;\n" "font-size: 16px;\n" "line-height: 19px;\n" "text-align: left;\n" "color: #FFFFFF;\n" "padding: 8px 0 8px 15px;") self.btn_menu2.setObjectName("btn_menu2") self.btn_menu3 = QtWidgets.QPushButton(self.buttons) self.btn_menu3.setGeometry(QtCore.QRect(0, 94, 219, 37)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.btn_menu3.sizePolicy().hasHeightForWidth()) self.btn_menu3.setSizePolicy(sizePolicy) self.btn_menu3.setMaximumSize(QtCore.QSize(219, 37)) self.btn_menu3.setStyleSheet("background: #282828;\n" "border-radius: 11px;\n" "font-family: \'Inter\';\n" "font-style: normal;\n" "font-weight: 400;\n" "font-size: 16px;\n" "line-height: 19px;\n" "text-align: left;\n" "color: #FFFFFF;\n" "padding: 8px 0 8px 15px;") self.btn_menu3.setObjectName("btn_menu3") self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget) self.stackedWidget.setGeometry(QtCore.QRect(269, -20, 977, 719)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth()) self.stackedWidget.setSizePolicy(sizePolicy) self.stackedWidget.setMinimumSize(QtCore.QSize(977, 719)) self.stackedWidget.setMaximumSize(QtCore.QSize(977, 719)) self.stackedWidget.setObjectName("stackedWidget") self.page_3 = QtWidgets.QWidget() self.page_3.setObjectName("page_3") self.stackedWidget.addWidget(self.page_3) self.page_4 = QtWidgets.QWidget() self.page_4.setObjectName("page_4") self.stackedWidget.addWidget(self.page_4) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) self.stackedWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.btn_menu1.setText(_translate("MainWindow", "Все задачи")) self.btn_menu2.setText(_translate("MainWindow", "Папка 1")) self.btn_menu3.setText(_translate("MainWindow", "Папка 2")) import src_rc 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *