Python: работа с именем модуля

Раздел: Модули -> Модули

Определение имени модуля в Python: основные подходы

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

Как определить, что модуль выполняется как главный скрипт, а не импортируется?

Наиболее распространённый и эффективный способ - проверка if __name__ == '__main__'. Этот блок выполняется только тогда, когда модуль запущен непосредственно (например, python mymodule.py). При импорте модуля __name__ принимает значение имени модуля (например, 'mymodule'), и условие оказывается ложным.


# module_check.py
print('Имя модуля:', __name__)
if __name__ == '__main__':
    print('Модуль запущен как главный скрипт')
else:
    print('Модуль импортирован')
  

Python имя модуля (имя модуля в python)

Результат при запуске python module_check.py:

Имя модуля: __main__
Модуль запущен как главный скрипт
  

При импорте import module_check:

Имя модуля: module_check
Модуль импортирован
  

Типичная ошибка:

Забыть добавить эту проверку в модули, которые должны выполнять код только при прямом запуске. Тогда при импорте модуля нежелательные действия (например, тестовые выводы) выполнятся автоматически. Рекомендуется всегда оборачивать запускаемый код в if __name__ == '__main__'.

Ещё одна проблема - случайное использование двойного подчёркивания с обеих сторон (__name__ обязательно с двумя подчёркиваниями). Ошибка if _name_ == '__main__' (одно подчёркивание) приведёт к синтаксической ошибке или неверному поведению.

Как получить имя текущего модуля в любом месте кода?

Переменная __name__ доступна в глобальной области видимости каждого модуля. Она всегда содержит строку с именем модуля (или '__main__' для главного скрипта).


# mymodule.py
def get_module_name():
    return __name__

print(get_module_name())  # 'mymodule'
  

Проблема:

При импорте модуля с псевдонимом (import mymodule as mm) mm.__name__ всё равно вернёт 'mymodule', а не псевдоним. Это ожидаемое поведение, но может сбить с толку новичков.

Как узнать имя модуля, в котором определена функция или класс?

У любой функции, метода или класса есть атрибут __module__, содержащий имя модуля, где объект был определён.


# module_a.py
def my_function():
    pass

class MyClass:
    pass

print(my_function.__module__)  # 'module_a'
print(MyClass.__module__)      # 'module_a'
  

Это полезно для рефлексии или логирования.

Ошибка:

Если функция определена в интерактивной сессии или в exec, __module__ может быть равен None или '__main__'.

Как получить имя файла модуля (путь к модулю)?

Атрибут __file__ содержит полный путь к файлу модуля, если он был загружен из файла. Для встроенных модулей или модулей, загруженных из C-расширений, __file__ может отсутствовать.


# mymodule.py
import os
print(os.path.basename(__file__))  # 'mymodule.py'
print(os.path.dirname(__file__))   # '/path/to/module'
  

Ошибка:

При запуске кода в интерактивной оболочке или из строки (например, python -c 'print(__file__)') атрибут __file__ может быть не определён, и возникнет NameError.

Как получить имя модуля из объекта, принадлежащего другому модулю?

Используется атрибут __module__ класса или функции.


# main.py
import mymodule
obj = mymodule.MyClass()
print(type(obj).__module__)  # 'mymodule'
  

Это помогает, например, при сериализации или динамической загрузке.

Как узнать имя модуля, из которого вызвана функция (имя модуля-отправителя)?

Модуль inspect позволяет анализировать стек вызовов. Функция inspect.getmodule(inspect.currentframe().f_back) вернёт модуль вызывающего кода. Затем можно получить его __name__.


import inspect

def caller_module_name():
    frame = inspect.currentframe().f_back
    module = inspect.getmodule(frame)
    return module.__name__ if module else None

# В другом модуле:
# print(caller_module_name())  # 'some_module'
  

Проблема:

Использование inspect снижает производительность и может быть небезопасным в многопоточном окружении. Также возможно получение None, если стек недоступен.

Как проверить, что модуль не является основным, и избежать выполнения кода при импорте?

Помимо проверки __name__ == '__main__', можно использовать альтернативу: if __name__ != '__main__' для кода, который должен выполняться только при импорте. Однако это редко требуется.


if __name__ != '__main__':
    print('Модуль импортирован, выполняем инициализацию')
  

Такой подход применяется для импортируемой конфигурации или регистрации.

Как именовать модули, чтобы избежать конфликтов? (PEP 8)

Имена модулей должны быть короткими, строчными, с использованием подчёркиваний для читаемости (например, my_module). Не рекомендуется использовать дефисы или точки. Следует избегать имён, совпадающих со стандартными модулями Python (например, string, os).


# Плохо:
my-Module.py   # дефис недопустим в import
# Хорошо:
my_module.py
  

Ошибка:

Импорт модуля с именем, начинающимся с цифры, вызовет синтаксическую ошибку. Например, файл 1module.py нельзя импортировать как import 1module.

Расширенные примеры работы с именем модуля

Пример: динамическая загрузка модуля и проверка его имени

Использование importlib для импорта модуля по строковому имени и последующая проверка его имени через __name__.

Пример

import importlib

module_name = 'os'
module = importlib.import_module(module_name)
print(f'Имя загруженного модуля: {module.__name__}')  # 'os'

# При импорте с псевдонимом
module_alias = importlib.import_module('os.path')
print(type(module_alias).__name__)  # 'module' (класс модуля, не имя)
print(module_alias.__name__)        # 'posixpath' или 'ntpath' в зависимости от ОС
  

Результат (на Windows):

Имя загруженного модуля: os
posixpath  # под Unix; на Windows будет 'ntpath'
  

Ошибка: если модуль не найден, import_module выбрасывает ModuleNotFoundError. Рекомендуется оборачивать в try/except.

Пример: использование __name__ для настройки логирования в зависимости от модуля

Стандартный модуль logging использует __name__ для создания логгера, что позволяет точно определить источник сообщения.

Пример

# logger_example.py
import logging

logger = logging.getLogger(__name__)

def do_something():
    logger.info('Выполнение функции do_something')

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)
    do_something()
    
# При импорте logger_example в другой скрипт:
# import logger_example
# logger_example.do_something()  # вывод: INFO:logger_example:Выполнение ...
  

Результат при прямом запуске:

INFO:__main__:Выполнение функции do_something
  

При импорте:

INFO:logger_example:Выполнение функции do_something
  

Типичная ошибка: забыть настроить базовый логгер (basicConfig) - тогда сообщения не отображаются. Также следует использовать __name__ вместо фиксированной строки.

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

В пакетах __name__ может содержать точечную нотацию (например, package.submodule). Это позволяет определить, как был импортирован модуль.

Пример

# package/submodule.py
print('__name__ =', __name__)  # 'package.submodule'
print('__package__ =', __package__)  # 'package'

# main.py в корне
from package.submodule import some_function
  

Результат при импорте python main.py:

__name__ = package.submodule
__package__ = package
  

Это можно использовать для относительных импортов.

Пример: получение имени модуля для объекта, созданного в другом модуле, с использованием inspect

Пример

# modules.py
def create_instance():
    class Inner:
        pass
    return Inner()

# main.py
import inspect
from modules import create_instance

obj = create_instance()
# Получаем модуль, где определён класс obj
class_of_obj = type(obj)
print('Класс определён в модуле:', class_of_obj.__module__)

# Либо через стёк
frame = inspect.currentframe().f_back
module = inspect.getmodule(frame)
print('Текущий модуль (main):', module.__name__ if module else None)
  

Результат:

Класс определён в модуле: modules
Текущий модуль (main): __main__
  

Важно: inspect.currentframe() может вернуть None в оптимизированном режиме (CPython с оптимизациями). Лучше использовать __module__ везде, где возможно.

Пример: создание регистратора плагинов с проверкой имени модуля

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

Пример

# plugin_registry.py
_registry = {}

def register(plugin_module):
    name = plugin_module.__name__
    if name in _registry:
        raise ValueError(f'Плагин с именем {name} уже зарегистрирован')
    _registry[name] = plugin_module
    return name

# plugin_alpha.py
from plugin_registry import register

def run():
    print('Alpha plugin')

register(__name__)  # передаём имя модуля

# main.py
import plugin_alpha  # регистрирует себя
  

Результат:

(без вывода, если регистрация успешна)
  

Ошибка: если модуль импортирован дважды (например, через разные пути), __name__ одинаковое, и регистрация может выбросить исключение. Лучше использовать id(module) или путь файла.

Пример: использование __name__ в тестировании (unittest)

Фреймворк unittest автоматически использует __name__ для запуска тестов. Можно добавить точку входа в тестовый модуль.

Пример

# test_example.py
import unittest

def test_simple():
    assert 1 + 1 == 2

if __name__ == '__main__':
    unittest.main()
  

При запуске python test_example.py тесты выполняются; при импорте import test_example - нет.

Имя модуля в Python - comments

En
Python имя модуля (python)