Skip to content

Latest commit

 

History

History
421 lines (335 loc) · 37.6 KB

README_packages.md

File metadata and controls

421 lines (335 loc) · 37.6 KB

Скрытые модули пакеты

Функции с нижним подчеркиванием _ не передаются в область видимости принимающего модуля при импорте.

Wildcard импорт:

from module_name import *

импортируется область глоб имен модуля. за исключением _function_name.

Папка __pycache__

При запуске пи.файла, в котором есть импорт модуля
import module, в корневой папке создастся папка __pycache__ в которую попадает скомпилированный модуль module в бинарнике(?)

В .gitignore необходимо добавить __pycache__/

Инструкция: [if __name__ == '__main__']:

Использовать везде:

def main():
    ...

    if __name__ == '__main__':
    nain()

в main() упаковывается сценарий работы модуля при его непосредственном запуске __name__ может иметь значения либо __main__ , либо имя_модуля (при импорте)

Аттрибуты (__builtins__ словарь переменных области видимости):

Результат вывода функции:

dir(module)

список с аттрибутами модуля. Интересующие вас значения, обычно, будут атрибутами и функциями, которые вы хотите использовать в своем коде, и вы сможете обращаться к ним, используя точечную нотацию, например, module.some_attribute или module.some_function().

__builtins__, __cached__ и другие атрибуты, которые вы видите в списке, это встроенные атрибуты модуля в Python. Они предоставляют информацию о модуле и его окружении. Например:

__builtins__: Этот атрибут ссылается на встроенный модуль Python, который содержит встроенные функции и объекты, такие как print(), len(), str(), и много других. Вы можете получить доступ к этим функциям и объектам, используя __builtins__.имя.

__cached__: Этот атрибут хранит путь к кэшированной (скомпилированной) версии модуля, если такая версия доступна. Он используется для ускорения повторных импортов модуля. Вы можете получить доступ к этому пути, чтобы узнать, где хранится скомпилированный модуль.

Для получения значений этих атрибутов, вы можете использовать обычную точечную нотацию:

import module
builtins_module = module.__builtins__
cached_path = module.__cached__
...

Обычно __builtins__ не рекомендуется модифицировать, так как это может привести к непредсказуемому поведению интерпретатора Python. Этот атрибут лучше всего использовать только для чтения встроенных функций и объектов.

__builtins__ - это словарь, который содержит встроенные функции, объекты и классы Python. Этот словарь предоставляет доступ ко всем стандартным функциям и объектам, которые доступны в Python без явного импорта. Внутри этого словаря находятся встроенные функции, такие как print(), len(), str(), а также встроенные исключения, такие как TypeError, ValueError, NameError и многие другие. Также в нем находятся базовые типы данных, такие как int, float, str, и так далее.

Например, если вы хотите использовать функцию len() или объект int, вы можете обращаться к ним через __builtins__ так:

length = __builtins__.len([1, 2, 3])
number = __builtins__.int("42")

В этом словаре также содержится информация о версии Python и различные системные настройки.

Таким образом, __builtins__ предоставляет доступ к стандартным инструментам и объектам Python, и они могут быть использованы без необходимости импорта.

Просто dir()

Просто dir() вернет глобальное пространство имен модуля в котором вызывается

Доступно из коробки:

import builtins
print(dir(builtins))

Пакеты

Пакет - способ организации модулей в одну структуру.

Импортирование модулей пакета через точку.

import package.module

Файлы __init__.py

Файлы __init__.py в пакетах Python играют важную роль в организации кода и делают каталог, содержащий эти файлы, интерпретатору Python понятным как пакет. Вот как они используются и для чего:

  • Определение Пакета: Когда вы создаете каталог и добавляете в него файл __init__.py, Python интерпретирует этот каталог как пакет. Это означает, что вы можете организовывать свой код в логические группы с использованием пакетов. Пакеты могут содержать другие модули (файлы .py) и даже другие подпакеты.

  • Выполнение инициализации: Файл __init__.py выполняется при импорте пакета. Это позволяет вам выполнять инициализацию кода, устанавливать переменные, создавать объекты или выполнять любые другие действия, которые необходимы для корректной работы вашего пакета.

  • Экспорт Объектов: Вы можете определить переменные, функции, классы или другие объекты в файле __init__.py, чтобы они были доступны извне пакета при импорте. Это позволяет вам управлять тем, какие объекты будут видны пользователю при импорте вашего пакета.

Файлы __init__.py могут быть пустыми, если вам не нужно выполнять какие-либо специфические действия при импорте пакета, но их наличие всё равно обязательно, чтобы Python интерпретировал каталог как пакет.

Прямой доступ к нужным частям пакета извне

Предположим, у вас есть следующая структура пакета:

my_package/
    __init__.py
    module1.py
    module2.py

В файле __init__.py вы можете определить, какие объекты будут доступны при импорте my_package:

# my_package/__init__.py
# Этот объект будет доступен при импорте my_package
package_variable = "Hello from my_package"
# Вы также можете импортировать объекты из других модулей внутри пакета
from .module1 import my_function

Теперь, при импорте my_package, переменная package_variable и функция my_function будут доступны извне пакета:

import my_package

print(my_package.package_variable)  # Вывод: Hello from my_package
my_package.my_function()  # Вызов функции из module1.py

атрибут __all__

Для того чтобы при импорте пакета можно было использовать только module1.py и не импортировать module2.py, вы можете использовать атрибут __all__ в файле __init__.py. Этот атрибут определяет список модулей и объектов, которые будут импортированы, если пакет импортируется с использованием from package import *. Ваш файл __init__.py может выглядеть следующим образом:

# my_package/__init__.py

__all__ = ['module1']

Теперь, при импорте пакета с использованием from my_package import *, будут доступны только объекты и модули, указанные в списке __all__ (в данном случае, только module1).

from my_package import *

print(package_variable)  # Вывод: Hello from my_package
my_function()  # Вызов функции из module1.py

# module2 не будет доступен, так как его нет в списке __all__
# module2.some_function()  # Это вызовет ошибку NameError

Этот способ ограничивает экспорт только теми модулями и объектами, которые указаны в __all__ и предостерегает от неожиданных импортов из пакета.

Ограничения импорта.

В Python, в файле __init__.py пакета, вы не можете прямо ограничить импорт определенных переменных и функций из модулей внутри пакета. Все имена, которые определены в модуле и не начинаются с символа подчеркивания (например, _private_name), по умолчанию будут доступны при импорте из пакета. Это следует из принципов языка Python, которые способствуют удобству и читаемости кода.

Однако, вы можете использовать соглашения и договоренности в вашем коде, чтобы обозначить, какие имена считать "публичными" и какие "приватными". Обычно в Python считается, что имена, начинающиеся с символа подчеркивания (например, _private_name), являются "приватными" и не предназначены для использования извне модуля. Это соглашение основано на конвенции и зависит от дисциплины программистов, но интерпретатор Python не накладывает жестких ограничений на доступ к именам.

Атрибут __all__ в файле __init__.py пакета применяется только к ограничению импорта определенных модулей из пакета, но не к ограничению импорта переменных и функций внутри этих модулей.

Атрибут __all__ определяет список модулей, которые будут импортированы, если вы используете оператор from package import *, и не влияет на доступность переменных и функций внутри этих модулей.

Для точного ограничения доступа к переменным и функциям внутри модулей, Python не предоставляет средств на уровне пакета, и рекомендуется полагаться на соглашения и документирование кода, чтобы явно указать, какие объекты считаются "приватными" и не предназначены для импорта извне.

Локальный импорт

Локальный импорт в init.py

from .module import function

Далее при импорте пакета получаем прямой доступ к локально импортированным объектам

import package
package.function()

Это нужно, чтобы в больших программах максимально прозрачно защищать пространство имен

"Дублирующий импорт"

Когда вы импортируете библиотеку, например LightGBM (LGBM), в одном модуле (назовем его mod1), а затем импортируете этот модуль (mod1) в другой модуль (mod2) и также импортируете LightGBM непосредственно в mod2, это не приводит к значительному увеличению затрат ресурсов вашего компьютера. Вот почему:

  • Механизм кэширования импорта в Python: Когда модуль импортируется в Python, он кэшируется (запоминается системой). Если модуль уже импортирован где-то в программе, повторные попытки импорта будут ссылаться на уже импортированный модуль, а не загружать его заново. Таким образом, даже если вы импортируете LGBM в mod1 и затем снова в mod2, фактический процесс импорта (загрузка и инициализация) происходит только один раз.
  • Поддержка модульности: Данная структура поддерживает модульность кода. Это значит, что каждый модуль может импортировать необходимые ему библиотеки, даже если они уже импортированы где-то еще. Это улучшает читаемость и поддерживаемость кода.
  • Незначительное увеличение времени загрузки: Единственный небольшой недостаток может заключаться в незначительном увеличении времени запуска программы, поскольку интерпретатору Python необходимо проверить, был ли модуль уже импортирован. Однако это обычно не оказывает заметного влияния на производительность.
  • Память: В отношении использования памяти, поскольку модуль загружается только один раз, не происходит дополнительного расхода памяти из-за такого «двойного» импорта.

Импорт внутри одной папки

Имеем, например, такую структуру:

Проект/
│
└──  ChiSpark/
    ├── logger.py
    └── enviserv.py

Чтобы импортировать файл logger.py в другой файл enviserv.py, который находится в той же папке проекта ChiSpark, вам нужно просто использовать инструкцию import logger в Python.

Импорт из других папок. Добавление папки в sys.path

Чтобы импортировать logger.py из папки ChiSpark в файл app.py, который находится в соседней папке src, вам нужно сначала убедиться, что Python может найти файл logger.py. Для этого структура каталогов должна быть правильно настроена.
Имеем, например, структуру:

Проект/
│
├── ChiSpark/
│   └── logger.py
│
└── src/
    └── app.py

Нужно добавить папку ChiSpark в sys.path:

# src/app.py
import sys
from pathlib import Path

# Добавление папки, содержащей ChiSpark, в sys.path
root_path = Path(__file__).parent.parent  # Это путь к папке 'Проект'
sys.path.append(str(root_path))

# Теперь можно импортировать logger из ChiSpark
from ChiSpark import logger

Использование абсолютных импортов.

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

Проект/
│
├── ChiSpark/
│   ├── __init__.py
│   └── logger.py
│
└── src/
    ├── __init__.py
    └── app.py

Чтобы использовать абсолютные импорты, Python должен знать, что папка Проект является корневой. Вот несколько способов, как это можно сделать:

  • Установка проекта как пакета: Если ваш проект имеет файл setup.py, установив его с помощью pip install ., Python будет распознавать папку, где находится setup.py, как корневую для импортов.

  • Использование переменной окружения PYTHONPATH: Вы можете добавить путь к папке Проект в переменную окружения PYTHONPATH. Это говорит Python искать модули для импорта сначала в этой директории.

  • Изменение sys.path во время выполнения: Хотя это и не рекомендуется, как постоянное решение, для временных целей или тестирования, вы можете добавить путь к папке Проект в sys.path в начале вашего скрипта.

После того как папка Проект будет распознана как корневая, вы можете использовать абсолютные импорты в app.py, например:

from ChiSpark import logger

Относительный импорт .. с помощью точечной нотации

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

Проект/
│
├── ChiSpark/
│   ├── __init__.py
│   └── logger.py
│
└── src/
    ├── __init__.py
    └── app.py

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

Теперь, чтобы импортировать logger.py из ChiSpark в app.py с использованием относительных импортов, код в app.py будет выглядеть следующим образом:

from ..ChiSpark import logger

Здесь .. означает "перейти на уровень вверх в иерархии папок". Это указывает Python, что он должен искать модуль logger в папке ChiSpark, которая находится на один уровень выше текущей папки (src).

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

Запуск модулей содержащими относительные импорты

Предположим, у вас есть следующая структура каталогов:

Проект/
│
├── ChiSpark/
│   ├── __init__.py
│   └── logger.py
│
└── src/
    ├── __init__.py
    └── app.py

Если вы используете относительный импорт в app.py:

from ..ChiSpark import logger

Этот импорт будет работать только в том случае, если app.py выполняется как часть пакета. Например, если у вас есть основной скрипт вне этих папок, который импортирует и запускает app.py, относительный импорт сработает. Однако, если вы попытаетесь напрямую запустить app.py как отдельный скрипт (например, python src/app.py), Python выдаст ошибку, так как относительные пути не работают в скриптах, запущенных напрямую.

Чтобы решить эту проблему, у вас есть несколько вариантов:

  • Использовать абсолютные импорты: Модифицировать структуру проекта так, чтобы папка Проект была корневой для вашего Python-проекта и использовать абсолютные пути. Например:
from ChiSpark import logger

В этом случае, вам нужно будет убедиться, что папка Проект находится в sys.path. Это можно сделать, добавив путь к этой папке в переменную окружения PYTHONPATH или модифицируя sys.path в вашем скрипте.

  • Запускать скрипт как модуль: Вы можете запускать app.py как модуль, что позволит использовать относительные импорты. Для этого, вам нужно будет запускать ваш скрипт из корневой папки Проект следующим образом:
python -m src.app

Этот подход позволяет Python интерпретировать src как пакет и использовать относительные импорты внутри app.py

Переменная окружения PYTHONPATH

Переменная окружения PYTHONPATH в Python используется для указания интерпретатору, где искать модули и пакеты при их импорте. Кроме решения задачи абсолютного импорта пакетов, PYTHONPATH выполняет несколько важных функций:

Поиск дополнительных библиотек: Если вы установили библиотеки Python в нестандартное место, которое не входит в стандартный путь поиска, вы можете использовать PYTHONPATH, чтобы указать Python, где искать эти библиотеки.

Работа с несколькими проектами: Если у вас есть несколько проектов с различными зависимостями, вы можете использовать PYTHONPATH, чтобы явно указать, какие папки должен использовать Python для поиска модулей для каждого проекта.

Разработка и тестирование: PYTHONPATH полезен при разработке и тестировании модулей, которые еще не были установлены (или установлены в разработческом режиме). Это позволяет легко тестировать изменения модулей без необходимости устанавливать их.

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

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

Важные соображения при использовании PYTHONPATH: Избегайте конфликтов: Добавление путей в PYTHONPATH может привести к конфликтам с существующими путями или модулями, особенно если в разных папках есть модули с одинаковыми именами.

Безопасность: Не добавляйте ненадежные или неизвестные директории в PYTHONPATH, так как это может представлять угрозу безопасности.

Портабельность: Зависимость от PYTHONPATH может снизить портабельность вашего кода, так как другие системы могут не иметь той же конфигурации.

Использование виртуальных сред: Виртуальные среды Python обычно управляют путями поиска модулей и могут конфликтовать с настройками PYTHONPATH. В таких случаях рекомендуется полагаться на механизмы виртуальной среды для управления зависимостями.

Установка значения PYTHONPATH на хост-машине

Добавление пути к папке вашего проекта в переменную окружения PYTHONPATH позволяет Python искать модули для импорта в этой директории. Вот как вы можете это сделать:

Для временного изменения PYTHONPATH на время текущей сессии терминала.
В командной строке Unix/Linux/MacOS:
Откройте терминал и выполните следующую команду, заменив /path/to/Проект на полный путь к папке вашего проекта:

export PYTHONPATH="/path/to/Проект:$PYTHONPATH"

В командной строке Windows:
Откройте командную строку и выполните:

set PYTHONPATH=C:\path\to\Проект;%PYTHONPATH%

Для постоянного изменения PYTHONPATH:
Unix/Linux/MacOS:
Добавьте строку export PYTHONPATH="/path/to/Проект:$PYTHONPATH" в файл .bashrc или .bash_profile (в зависимости от вашей системы и конфигурации) в домашней директории вашего пользователя. После этого вам нужно будет перезагрузить терминал или выполнить source ~/.bashrc (или соответствующий файл), чтобы применить изменения.

Windows:
Откройте "Система" в "Панели управления". Перейдите к "Дополнительные системные настройки" и откройте "Переменные среды".
Найдите переменную PYTHONPATH в списке системных переменных и добавьте в её значение путь к папке вашего проекта. Если переменная не существует, создайте её и укажите соответствующий путь.
Перезагрузите компьютер или перезапустите любые программы, которым необходим доступ к обновлённой переменной PYTHONPATH. Важные замечания:
Убедитесь, что путь, который вы добавляете, точно соответствует каталогу, который содержит ваш проект.
Изменения PYTHONPATH могут повлиять на другие Python-проекты и среды, поэтому будьте осторожны, особенно при внесении постоянных изменений. Если вы используете виртуальные среды, учитывайте, что изменения PYTHONPATH могут быть несовместимы с некоторыми из них, и может потребоваться другой подход.

Пути импорта библиотек.

Установка переменной окружения PYTHONPATH не препятствует импорту модулей из стандартной библиотеки Python. PYTHONPATH используется для дополнения стандартного пути поиска модулей, а не для его замены.

Когда Python запускается, он автоматически создает список директорий, в которых будет искать модули при выполнении импорта. Этот список включает:

Директория, из которой запущен скрипт (или текущая директория, если Python используется в интерактивном режиме).

Директории, указанные в PYTHONPATH (если таковые имеются). Это пути, которые вы можете настроить, чтобы указать Python, где искать дополнительные модули и пакеты.

Стандартные директории, в которых Python ищет свои встроенные модули и пакеты стандартной библиотеки.

Изменение PYTHONPATH добавляет дополнительные пути в этот список, но не удаляет и не переопределяет стандартные пути, где хранятся модули стандартной библиотеки Python. Таким образом, вы все равно сможете импортировать стандартные модули, такие как os, sys, json и другие, даже если установите PYTHONPATH.

Чтобы увидеть полный список путей, где Python ищет модули, вы можете вывести содержимое sys.path в вашем скрипте или интерактивной сессии:

import sys
print(sys.path)

Текущее значение PYTHONPATH

Для Unix/Linux/MacOS:
Использование терминала:
Откройте терминал и введите следующую команду:

Copy code
echo $PYTHONPATH

Для Windows:
Откройте командную строку (cmd) и введите:

echo %PYTHONPATH%

Использование Python:
Независимо от операционной системы, вы также можете проверить PYTHONPATH непосредственно из Python:

import os
print(os.environ.get('PYTHONPATH'))

Если PYTHONPATH не установлен, результат будет None.

Установка PYTHONPATH в докер контейнере

На примере проекта chicago_spark.
Чтобы установить значение переменной окружения PYTHONPATH в контейнере jupyter_lab таким же, как "$PATH_TO_PROJECT_DIR:/work", вам нужно модифицировать инструкцию запуска контейнера jupyter_lab в вашем скрипте создания/запуска кластера, добавив определение переменной окружения PYTHONPATH с помощью параметра -e или --env в команде docker run.

run_container "jupyter_lab" \
  "-d --name jupyter_lab -p 10000:8888 --network spark_network --user root \
  -v $PATH_TO_PROJECT_DIR:/work:rw \
  -e SPARK_MASTER_IP=$SPARK_MASTER_IP \
  -e SPARK_MASTER=spark://$SPARK_MASTER_IP:7077 \
  -e PYTHONPATH=/work \
  jupyter/pyspark-notebook start-notebook.sh \
  --NotebookApp.token='' --NotebookApp.notebook_dir='/work'"

Строка -e PYTHONPATH=/work устанавливает PYTHONPATH в контейнере jupyter_lab равным /work. Это позволит Python внутри контейнера искать модули в этой директории

Остановите и удалите существующий контейнер

docker stop jupyter_lab
docker rm jupyter_lab

Запустите контейнер заново с новыми параметрами. Используйте обновленный скрипт запуска

Список значений для PYTHONPATH

Переменная окружения PYTHONPATH может содержать список путей, разделенных двоеточием (в Unix/Linux/MacOS) или точкой с запятой (в Windows). Это позволяет указать Python несколько каталогов, в которых следует искать модули при их импорте.

Пример использования PYTHONPATH с несколькими путями: В Unix/Linux/MacOS:

export PYTHONPATH="/path/to/directory1:/path/to/directory2"

Это установит PYTHONPATH так, чтобы Python искал модули сначала в /path/to/directory1, а затем в /path/to/directory2.

В Windows:

set PYTHONPATH=C:\path\to\directory1;C:\path\to\directory2

Пути в PYTHONPATH проверяются в том порядке, в котором они указаны. Если один и тот же модуль присутствует в нескольких каталогах, Python будет использовать первый найденный экземпляр.

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

Используйте абсолютные пути для ясности и избегайте использования относительных путей в PYTHONPATH, чтобы избежать неоднозначности.

Не забудьте включить стандартные пути поиска Python в PYTHONPATH, если это необходимо. Обычно это не требуется, так как Python автоматически включает стандартные пути.