okpython.net
Все для начинающих

Модули и пакеты в Python

Понятие модуля в Python

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

Модуль (от англ. module) – это отдельный файл с программным кодом на языке Python, который создается один раз и далее может быть использован программами многократно.

Для формирования модуля необходимо создать обычный текстовый файл с расширением *py и записать в него целевые программные инструкции. Название файла при этом будет представлять имя модуля, а сам модуль после создания станет доступным для использования либо в качестве независимого сценария, либо в виде расширения, подключаемого к другим модулям, позволяя тем самым связывать отдельные файлы в крупные программные системы. При этом все имена, которым будет выполнено присваивание на верхнем уровне модуля (т.е. внутри файла модуля вне функций, классов и т.д.), становятся атрибутами объекта модуля, доступными для использования клиентами в привычном нам формате mod_name.attr.

Использование модульного подхода в программировании дает ряд важных преимуществ, т.к. модули:

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

Все модули в Python можно разделить на четыре основные категории:

  • встроенные модули (от англ. bilt-in) – представляют собой базовые возможности языка и либо импортируются интерпретатором автоматически, либо требуют лишь простого импортирования без необходимости дополнительной установки;
  • стандартная библиотека (от англ. standard library) – обширная коллекция дополнительных модулей и пакетов, представляющих расширенные возможности языка, которая входит непосредственно в состав дистрибутива Python и также требует лишь простого импортирования ее модулей без необходимости дополнительной установки;
  • сторонние модули (от англ. 3rd party) – более 90 000 модулей и пакетов, которые не входят в дистрибутив Python, но могут быть установлены из Python Package Index (PyPI) официального сайта с помощью утилиты pip;
  • пользовательские модули – все модули программы, которые создаются самими разработчиками.

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

Инструкции import и from в Python

Для использования дополнительных модулей в коде основного их сперва нужно к нему подключить, т.е. импортировать. Сделать это можно при помощи инструкции import, после которой через пробел следует указать имя импортируемого модуля. Если модулей несколько, допускается перечислять их после ключевого слова через запятую, однако рекомендуется использовать несколько отдельных инструкций импорта по одной в каждой строке. Указывать расширения файлов модулей при импорте не нужно (см. пример №1).

Код Результат pythonCodes
# Импортируем два модуля, используя по одной инструкции
# в каждой строке, опуская расширения файлов модулей.
import math
import random        
# Импортировать сразу два модуля, перечисляя их 
# через запятую, можно, но не рекомендуется PEP8.
# import math, random

# Теперь доступ к переменным в модулях открыт:
# они доступны нам в виде атрибутов объектов модулей.
a = math.pi
b = random.randint(1, 10)        
        
# Выводим случайное произведение на экран.
print(a*b)
6.283185307179586












		
			

Пример №1. Использование инструкции import (часть 1).

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

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

Если по каким-то причинам вам придется импортировать модули в теле функции или внутри класса, следует иметь в виду, что все переменные модуля будут импортированы либо в локальную область видимости функции, либо в дерево атрибутов класса (см. пример №2).

Код Результат pythonCodes
# Определяем польз. функцию.
def my_func():
    # Импортируем модуль в локальную 
    # область видимости функции.
    import math
    # Выводим результат на экран.
    print(math.sqrt(25))        
 
# Выведет 5.
my_func()
# Ошибка: такое имя отсутствует в 
# глобальной области видимости.
# print(math.sqrt(25))         

class MyClass:
    # Импортируем модуль в область   
    # видимости класса (дерево атрибутов).
    import random
    # Атрибут класса.
    a = 100

# Создаем экземпляр класса.             
obj = MyClass()
# Выводим случайное число на экран.
print(obj.random.randint(1, 15))        
print(obj.random.randint(1, 15))        
print(obj.random.randint(1, 15))
5.0
2
10
15





















		
			

Пример №2. Использование инструкции import (часть 2).

Старайтесь избегать инструкций import в теле функции, т.к. это будет приводить к многократным дорогостоящим операциям импортирования при каждом новом вызове функции в виду очистки памяти после выхода из нее. Именно из-за дороговизны импорта любой дополнительный модуль по умолчанию подгружается только один раз за все время работы программы. При повторных попытках импорта интерпретатор по умолчанию просто использует модуль, уже загруженный в память.

В случаях, когда имя импортируемого модуля оказывается слишком длинным, конфликтует с именем в целевом сценарии либо не устраивает нас по каким-то другим причинам, разрешается заменять его собственным вариантом имени (псевдонимом) при помощи расширения инструкции import ключевым словом as (см. пример №3).

Код Результат pythonCodes
# Используем короткие псевдонимы.
import math as m
import random as rnd       
# Через запятую не рекомендуется PEP8.
# import math as m, random as rnd

# Теперь модули доступны по их псевдонимам.
a = m.pi
b = rnd.randint(1, 10)        

# А имя math осталось свободным.
math = 5

# Выводим случайное произведение на экран.
print(math*a*b)
125.66370614359172












		
			

Пример №3. Использование инструкции import (часть 3).

Как не трудно догадаться, инструкция import long_module_name as name является удобным сокращением блока инструкций import long_module_name, name = long_module_name, del long_module_name, в котором ссылка на импортированный объект модуля присваивается короткому имени, а исходное длинное имя с той же, но уже ненужной ссылкой, просто удаляется.

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

  • from module import name – из указанного модуля будет импортировано лишь одно конкретное имя;
  • from module import name_1, name_2, ... – из указанного модуля будут импортированы все перечисленные имена;
  • from module import very_long_name as name – используем для импортируемого имени псевдоним;
  • from module import long_name_1 as name_1, long_name_2 as name_2, ... – используем для импортируемых имен псевдонимы;
  • from module import * – все имена из указанного модуля будут импортированы в целевой модуль (см. пример №4);
  • from __future__ import new_name – специальная инструкция импорта, позволяющая включить расширения языка, которые предусмотрены в его будущих версиях, но пока по умолчанию отключены для текущей (здесь мы рассматривать этот вариант не будем).
Код Результат pythonCodes
# Импортируем из модуля все имена.
from random import *
# Импортируем из модуля лишь одно имя.
from importlib import reload
# Импортируем только указанные имена, используя для 
# некоторых из них псевдонимы (PEP8 не возражает).
from math import degrees as dg, pi, factorial as fctl
        
# Импортированные инструкцией from имена могут 
# использоваться без указания имен их модулей.
# Теперь просто pi вместо math.pi.
print('pi:', pi)
# fctl(5) вместо math.factorial(5).
print('fctl(5):', fctl(5))
# randint(1, 10) вместо random.randint(1, 10).
print('randint(1, 10):', randint(1, 10))
pi: 3.141592653589793
fctl(5): 120
randint(1, 10): 9











		
			

Пример №4. Использование инструкции from (часть 1).

Как видим, использование инструкции from позволяет использовать скопированные имена напрямую, не предваряя их именем модуля. Однако следует иметь в виду, что имена, копируемые инструкцией from, становятся ссылками на разделяемые объекты, на которые могут ссылаться сразу несколько модулей. Поэтому воизбежание ошибок и непреднамеренных изменений значений переменных в импортированном модуле повторное присваивание полученному имени не оказывает воздействия на модуль, откуда это имя было скопировано. Однако, если разделяемый объект является изменяемым (например, списком или словарем), то его модификация может оказывать воздействие и на объект в импортированном модуле (см. пример №5).

Код Результат pythonCodes
# Пусть в каталоге с основным модулем mod_2.py
# имеется дополнительный модуль mod_1.py. 
              
# ------------ mod_1.py ----------------- 

# Неизменяемый тип данных.
num = 10
# Изменяемый тип данных.
li = [1, 2]

# Ф-ция работает с переменными в mod_1.py.
def print_info(): 
    # Выводим значение num.
    print('num в mod_1:', num)     
    # Выводим значение li.
    print('li в mod_1:', li, end='\n\n')


# ------------ mod_2.py ----------------- 

# Импортируем имена из mod_1.py с помощью from.
from mod_1 import num, li, print_info

# num в mod_1: 10, li в mod_1: [1, 2].
print_info()  

# num в mod_2: 10. 
print('num в mod_2:', num)         
# li в mod_2: [1, 2]. 
print('li в mod_2:', li, end='\n\n')         
    
print('- Изменяем num и модифицируем li -\n')

# Изменяя значение num здесь, т.е. в mod_2, 
# мы не влияем на значение num в mod_1. 
num = 15
# Но лишь частично модифицируя изменяемый объект li 
# в mod_2, мы также модифицируем его и в mod_1. 
li[1] = 20    

# num в mod_1: 10, т.е. осталось прежним.
# li в mod_1: [1, 20], т.е. изменилось.   
print_info()        

# num в mod_2: 15, т.е. здесь изменилось.
print('num в mod_2:', num)
# li в mod_2: [1, 20], т.е. также изменилось.
print('li в mod_2:', li)
num в mod_1: 10
li в mod_1: [1, 2]

num в mod_2: 10
li в mod_2: [1, 2]

- Изменяем num и модифицируем li -

num в mod_1: 10
li в mod_1: [1, 20]

num в mod_2: 15
li в mod_2: [1, 20]

































		
			

Пример №5. Использование инструкции from (часть 2).

Таким образом, при использовании инструкции from нужно стараться избегать необоснованного изменения переменных в дополнительных модулях, особенно в случаях импортирования одного и того же имени сразу несколькими модулями. Если же значение в дополнительном модуле все таки требуется изменить, лучше использовать абсолютное импортирование с указанием имени в формате mod_name.var.

Также следует отметить, что в отличие от инструкции import использовать инструкцию from ... * в локальной области видимости функций запрещается, в то время как остальные вариации этой инструкции вполне допустимы (см. пример №6).

Код Результат pythonCodes
# Определяем польз. функцию.
def print_pi():
    # Импортируем переменную в локальную 
    # область видимости ф-ции (так можно).
    from math import pi
    
    # А так запрещено!
    # from math import *             
    
    # Выводим результат на экран.
    print('Число Пи:', round(pi, 3))        
    
# Выведет 5.
print_pi()
       
class MyClass:
    # Импортируем переменную в область   
    # видимости класса (дерево атрибутов).
    from math import pi

    # А так запрещено!
    # from math import *              

# Создаем экземпляр класса.             
obj = MyClass()
# Выводим значение Пи на экран.
print('Число Пи:', obj.pi)
Число Пи: 3.142
3.141592653589793























		
			

Пример №6. Использование инструкции from (часть 3).

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

Путь поиска модулей в Python

Во всех примерах выше при импортировании модулей мы полагались на то, что интерпретатор автоматически найдет нужные нам модули. Это связано с тем, что Python выполняет поиск модулей и пакетов не хаотически, а в соответствии с установленным порядком: сперва проверяется наличие импортируемого имени во встроенном модуле, после чего поочередно проверяются каталоги, список которых содержится в переменной sys.path. Данный список включает:

  • домашний каталог программы, который в поиске участвует всегда, автоматически добавляясь в начало списка (если код запускается в интерактивном сеансе, в начало списка добавляется пустая строка);
  • содержимое переменной окружения PYTHONPATH, если она задана;
  • каталог стандартной библиотеки, который также всегда участвует в поиске;
  • список каталогов, настроенных во время установки Python;
  • содержимое любых файлов с расширением *.pth, если они используются.
Точное содержимое переменной sys.path конечно же зависит от конкретной среды, поэтому для получения актуальной информации нужно импортировать модуль sys и вывести содержимое переменной на экран (см. пример №7).

Код Результат pythonCodes

# Я поместил файл my.pth в C:\\python и прописал 
# в нем каталог D:\python\sveta, именно поэтому 
# он в списке sys.path идет после C:\\python,
# но перед C:\\python\\lib\\site-packages.

# Импортируем модуль sys.
import sys
         
# Выводим имена встроенных модулей
print(sys.builtin_module_names, end='\n\n')
# и остальные пути поиска интерпретатора.
print('sys.path:', sys.path)
('_abc', '_ast', '_bisect', '_blake2', '_codecs', '_codecs_cn', '_codecs_hk', 
'_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw', '_collections', 
'_contextvars', '_csv', '_datetime', '_functools', '_heapq', '_imp', 
'_io', '_json', '_locale', '_lsprof', '_md5', '_multibytecodec', '_opcode', 
'_operator', '_pickle', '_random', '_sha1', '_sha256', '_sha3', '_sha512', 
'_signal', '_sre', '_stat', '_statistics', '_string', '_struct', '_symtable', 
'_thread', '_tracemalloc', '_warnings', '_weakref', '_winapi', '_xxsubinterpreters', 
'array', 'atexit', 'audioop', 'binascii', 'builtins', 'cmath', 'errno', 
'faulthandler', 'gc', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', 'nt', 
'sys', 'time', 'winreg', 'xxsubtype', 'zlib')

sys.path: ['D:\\python\\обучение', 'C:\\python\\python310.zip', 'C:\\python\\DLLs', 
'C:\\python\\lib', 'C:\\python', 'D:\\python\\sveta', 'C:\\python\\lib\\site-packages', ]

Пример №7. Путь поиска модулей (часть 1).

Как видим, первым в списке идет каталог нашей программы, а также присутствует каталог с модулями стандартной библиотеки. Они всегда автоматически включаются в путь поиска интерпретатора, поэтому до тех пор, пока мы размещаем все наши дополнительные модули и пакеты программы в ее корневой папке, нам не нужно беспокоится о каких-либо настройках пути поиска интерпретатора. Позже, когда вы начнете писать объемные проекты, вам наверняка придется настраивать переменную окружения PYTHONPATH или использовать файлы *.pth. Сейчас же мы не будем вдаваться в эти вопросы подробно, перечислив лишь основные моменты, на которые следует обратить внимание при импорте и настройке путей поиска модулей и пакетов. Итак.

  • Поскольку домашний каталог программы расположен первым в списке sys.path, не следует называть собственные модули или пакеты аналогично поставляемым с дистрибутивом Python, т.к. интерпретатор обнаружит пользовательские модули первыми и даже не будет пытаться продолжать поиск в каталоге стандартной библиотеки (см. пример №8).
  • Если необходимость в импортировании модуля за пределами домашней папки и вне прописанных путей поиска все-таки возникает, разрешается использовать обычный текстовый файл с расширением *.pth, в котором нужно прописать добавляемые каталоги по одному в каждой строке, а сам файл поместить либо в каталог установки Python (у меня, например, это C:\python), либо в подкаталог site-packages стандартной библиотеки (у меня, например, это C:\python\lib\site-packages). В результате все перечисленные в файле и при этом существующие каталоги будут добавлены в путь поиска модулей в порядке очередности, что можно будет увидеть в списке переменной sys.path: в первом случае каталоги будут добавлены после каталога установки Python, но перед подкаталогом site-packages стандартной библиотеки (еще раз посмотрите пример №7), а во втором – после подкаталога site-packages.
  • При необходимости сценарии могут самостоятельно изменять список sys.path, тем самым задавая путь поиска для всех последующих операций импорта. Однако нужно помнить, что все изменения продолжат действовать лишь до завершения программы, в то время как переменная окружения PYTHONPATH и файлы *.pth обеспечивают возможность более долговременного хранения измененного пути.
  • Если модуль найти не удается, возбуждается исключение ModuleNotFoundError. При ошибке загрузки существующего модуля – ImportError.
Код Результат pythonCodes
# Пусть у нас есть два собственных дополнительных
# модуля в каталоге с основным модулем программы. 

# 1-й дополнительный модуль random.py.
# Переопределяем в нем функцию модуля random.
def randint(a, b): return 55

# 2-й дополнительный модуль math.py.
# Переопределяем в нем число Пи.
pi = 0        

# -------------------------------------------------------

# Импор-тся наш модуль, а не стандартной библиотеки.
# Никогда не используйте имена модулей библиотеки!
import random
# Импортируется встроенный модуль, а не наш.
# Никогда не используйте зарезерв-ные имена модулей!
import math        
        
# Всегда будет выводить 55 из нашего модуля.
print(random.randint(1, 10))
# А здесь наоборот, не будет виден наш модуль.
print(math.pi)        

# ModuleNotFoundError: No module named mattth.
import mattth 
55
3.141592653589793
No module named 'mattth'
























Пример №8. Путь поиска модулей (часть 2).

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

Пакеты модулей в Python

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

Пакет (от англ. package) – это отдельный каталог в Python, в котором содержатся модули и другие пакеты, а также обязательный файл __init.py__, отвечающий за инициализацию пакета.

Импортировать пакеты практически также легко, как и модули. Только при этом в инструкции import нужно указывать путь к требуемому модулю пакета, используя для разделения обычные точки (см. пример №9).

Код Результат pythonCodes
# Будем считать, что в корневом каталоге dir_0 нашей программы
# мы создали 2 вложенных каталога dir_0/dir_1/dir_2 и dir_0/dir_1/dir_3. 
# Чтобы превратить всю структуру в пакеты, мы поместили в
# dir_1, dir_2 и dir_3 по пустому файлу __init__.py и,
# соответствующие файлы (т.е. модули) file_1.py, file_2.py и file_3.py,  
# прописав в них инструкции var_1 = 1, var_2 = 2 и var_3 = 3.

# В корневой папке dir_0 файл инициализации __init__.py не нужен!    
        
# -------------------------------------------------------

# Импортируем модуль, получая цепочку вложенных объектов.
import dir_1.dir_2.file_2
# <module dir_1 from D:\\python\\обучение\\dir_1\\__init__.py>.
print(dir_1)         
# <module dir_1.dir_2 from D:\\python\\обучение\\dir_1\\dir_2\\__init__.py>.
print(dir_1.dir_2)        
# <module dir_1.dir_2.file_2 from D:\\python\\обучение\\dir_1\\dir_2\\file_2.py>.
print(dir_1.dir_2.file_2)
# И добрались до переменной.
print('var_2:', dir_1.dir_2.file_2.var_2, end='\n\n')             
# Подчищаем пространство имен.
del dir_1       
        
# Используем для модуля псевдоним.
import dir_1.dir_3.file_3 as fl_3
# <module dir_1.dir_3.file_3 from D:\\python\\обучение\\dir_1\\dir_3\\file_3.py>.
print(fl_3)
# Обращаемся к переменной (т.е. атрибуту модуля).
print('var_3:', fl_3.var_3, end='\n\n')         
# Из-за псевдонима объекты пакетов цепочки станут недоступны.
# name dir_1 is not defined
# print(dir_1)         
# Подчищаем пространство имен.
del fl_3 
        
# Импортируем из модуля конкретное имя.
from dir_1.file_1 import var_1
# Как и ожидалось, вывело var_1: 1.
print('var_1:', var_1) 
<module 'dir_1' from 'D:\\python\\обучение\\dir_1\\__init__.py'>
<module 'dir_1.dir_2' from 'D:\\python\\обучение\\dir_1\\dir_2\\__init__.py'>
<module 'dir_1.dir_2.file_2' from 'D:\\python\\обучение\\dir_1\\dir_2\\file_2.py'>
var_2: 2

<module 'dir_1.dir_3.file_3' from 'D:\\python\\обучение\\dir_1\\dir_3\\file_3.py'>
var_3: 3

var_1: 1





























		
			

Пример №9. Импортирование пакетов (часть 1).

В нашем примере программа имеет следующую файловую структуру:

dir_0/  # Корневая директория программы.
	
	main.py  # Основной модуль программы.
	
	dir_1/   # Корневой каталог пакета.
		__init__.py   # Файл инициализации для dir_1.
		file_1.py      # import dir_1.file_1.
		  
		dir_2/  # Вложенный в dir_1 каталог пакета. 
			__init__.py  # Файл инициализации для dir_2. 	
			file_2.py     # import dir_1.dir_2.file_2.
		  
		dir_3/  # Вложенный в dir_1 каталог пакета. 
			__init__.py  # Файл инициализации для dir_3. 	
			file_3.py     # import dir_1.dir_3.file_3.	
    

Как видим, для создания структуры пакета необходимо создать каталог с подкаталогами и в каждом из них расположить обязательный специальный файл инициализации __init.py__, инструкции которого будут выполняться каждый раз при первичном импорте пакета или подпакета, а также файлы с python-кодом, которые будут представлять собой модули пакета и его подпакетов (их инструкции, кстати, тоже выполняются при первичном импорте).

После того, как пакет будет создан, любой из его модулей или отдельных имен в них могут быть подключены при помощи обычных инструкций импорта import или from, но с одним важным дополнением: вместо одиночного имени модуля необходимо прописывать полный путь к нему, начиная от корневого каталога пакета и отделяя вложенные пакеты с помощью точечной нотации. Так для импортирования модуля file_2 мы использовали инструкцию import dir_1.dir_2.file_2, а для импортирования единственного имени var_1 из модуля file_1 – инструкцию from dir_1.file_1 import var_1.

Следует отметить, что после импортирования модуля или подпакета все элементы пути, такие как корневой каталог, подкаталоги и сам модуль, автоматически становятся цепочкой вложенных друг в друга объектов в виде значений соответствующих атрибутов. Благодаря этому ко всем объектам цепочки можно обратиться обычным для атрибутов способом, т.е. при помощи точечной нотации. Так после импорта import dir_1.dir_2.file_2 мы смогли получить доступ как к пакету dir_1, так и подпакету dir_1.dir_2, что позволило нам вывести их строковые представления на экран.

Поскольку путь к модулю временами может получаться довольно длинным, разрешается использовать привычное нам расширение as. В нашем примере мы сократили длинное имя в инструкции import dir_1.dir_3.file_3 as fl_3, воспользовавшись псевдонимом fl_3 и заменив всю возможную цепочку вложенных объектов единственным объектом, связанным с пседнонимом (как следствие, обратиться к имени dir_1, например, уже не получится). Точно также мы могли бы использовать псевдоним и в инструкции from, прописав, например, from dir_1.file_1 import var_1 as v_1.

Что касается пакетов и подпакетов, то их разрешается импортировать точно также, как и обычные модули. При этом стоит иметь в виду, что в процессе импорта все инструкции их файлов инициализации __init.py__ выполняются в порядке вложенности пакетов, а их глобальные переменные автоматически становятся доступными в целевом модуле в качестве атрибутов импортированных имен (см. пример №10).

Код Результат pythonCodes
# Будем считать, что в домашнем каталоге программы
# мы создали пакет dir_1 с модулем file_1.py и 
# вложенный пакет dir_2 с модулем file_2.py. 

# Инструкции __init__.py в dir_1.
# var_init_1 = 'in dir_1.__init__'
# print('Импортируется пакет:'__name__)

# Инструкции file_1.    
# var_1 = 1
        
# Инструкции __init__.py в dir_2.    
# var_init_2 = 'in dir_1.dir_2.__init__' 
# print('Импортируется пакет:', __name__, end='\n\n')

# Инструкции file_2.    
# var_2 = 2

# -------------------------------------------------------

# Импортируем модуль, получая цепочку вложенных объектов.
import dir_1.dir_2

# <module dir_1 from D:\\python\\обучение\\dir_1\\__init__.py>.
print(dir_1)         
# Выведет in dir_1.__init__.
print(dir_1.var_init_1, end='\n\n')
# Автоматически импортируются только файлы __init__!
# module dir_1 has no attribute file_1
# print(dir_1.file_1)         

# <module 'dir_1' from 'D:\\python\\обучение\\dir_1\\__init__.py'>.
print(dir_1.dir_2)           
# Выведет in dir_1.dir_2.__init__.
print(dir_1.dir_2.var_init_2)         
# Автоматически импортируются только файлы __init__!
# module dir_1.dir_2 has no attribute file_2
print(dir_1.dir_2.file_2)
Импортируется пакет: dir_1
Импортируется пакет: dir_1.dir_2

<module 'dir_1' from 'D:\\python\\обучение\\dir_1\\__init__.py'>
in dir_1.__init__

<module 'dir_1.dir_2' from 'D:\\python\\обучение\\dir_1\\dir_2\\__init__.py'>
in dir_1.dir_2.__init__
module 'dir_1.dir_2' has no attribute 'file_2'



























		
			

Пример №10. Импортирование пакетов (часть 2).

Как видим, обычные модули пакетов автоматически не импортируются, а значит и обратиться к ним в импортирующем модуле посредством имени пакета не получится. Каждый модуль должен быть импортирован отдельно либо соответствующие инструкции должны быть прописаны в файлах инициализации (см. пример №11).

Код Результат pythonCodes
# Будем считать, что в домашнем каталоге программы
# мы создали пакет dir_1 с модулем file_1.py и 
# вложенный пакет dir_2 с модулем file_2.py. 

# Инструкции __init__.py в dir_1.
# Внутри пакета пути пишем полностью!
# import dir_1.file_1
# import dir_1.dir_2.file_2

# Инструкции модуля file_1.    
# var_1 = 1
# Инструкции модуля file_2.    
# var_2 = 2

# ----------------------------------------------------

# Импортируем пакет.
import dir_1

# Вот теперь доступны все подпакеты и модули, которые 
# были импортированы в файл инициализации пакета dir_1.
print(dir_1.file_1)
print(dir_1.file_1.var_1)
print(dir_1.dir_2)           
print(dir_1.dir_2.file_2)
print(dir_1.dir_2.file_2.var_2)
<module 'dir_1.file_1' from 'D:\\python\\обучение\\dir_1\\file_1.py'>
1
<module 'dir_1.dir_2' from 'D:\\python\\обучение\\dir_1\\dir_2\\__init__.py'>
<module 'dir_1.dir_2.file_2' from 'D:\\python\\обучение\\dir_1\\dir_2\\file_2.py'>
2



















		
			

Пример №11. Импортирование пакетов (часть 3).

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

Импортирование относительно пакета в Python

В случае импортирования подпакетов и модулей внутри пакетов для инструкции from <path> import <name> имеется возможность использовать относительный путь вместо абсолютного. В этом случае для указания текущего пакета, в котором находится модуль осуществляющий операцию импорта, используется одна ведущая точка, а для указания подпакета, расположенного на один уровень вверх, используются две ведущие точки. Соответственно, три ведущие точки означают подпакет на два уровня выше и т.д. Главное, чтобы все каталоги являлись подпакетами, т.е. находились внутри корневой папки всего пакета, и содержали файл инициализации __init.py__. Тоже самое касается и модулей, т.к. использовать относительный путь для импорта модуля вне пакета не получится (см. пример №12).

Код Результат pythonCodes
# Будем считать, что в домашнем каталоге программы
# мы создали пакет dir_1 с модулем file_1.py,  
# в нем пакет dir_2 с модулем file_2.py и в нем
# пакет dir_3 с модулем file_3.py. 
       
# Инструкции модуля file_1.    
# var_1 = 1

# Инструкции модуля file_2.    
# Импортируем имя var_1 из модуля file_1 в dir_1.
# from ..file_1 import var_1         
# Импортируем имя var_3 из модуля file_3 в dir_3.
# from .dir_3.file_3 import var_3         
# var_2 = 2
        
# Инструкции модуля file_3.    
# Импортируем имя var_1 из модуля file_1 в dir_1.
# from ...file_1 import var_1 
# var_3 = 3        
        
# ----------------------------------------------------

# Импортируем модули.
import dir_1.file_1 as mod_1       
import dir_1.dir_2.file_2 as mod_2 
import dir_1.dir_2.dir_3.file_3 as mod_3 

# Абсолютный импорт одной переменной.
from dir_1.dir_2.dir_3.file_3 import var_3         
# Относительный импорт вне пакета запрещен!
# from .dir_1.dir_2.dir_3.file_3 import var_3         
        
# Выводим переменные первого модуля. 
print('mod_1.var_1:', mod_1.var_1, end='\n\n')

# Выводим переменные второго модуля. 
print('mod_2.var_1:', mod_2.var_1)
print('mod_2.var_2:', mod_2.var_2)
print('mod_2.var_3:', mod_2.var_3, end='\n\n')       

# Выводим переменные третьего модуля. 
print('mod_3.var_1:', mod_3.var_1)
print('mod_3.var_3:', mod_3.var_3)          
print('var_3:', var_3)
mod_1.var_1: 1

mod_2.var_1: 1
mod_2.var_2: 2
mod_2.var_3: 3

mod_3.var_1: 1
mod_3.var_3: 3
var_3: 3

































		
			

Пример №12. Импортирование относительно пакета.

Обратите внимание, что с круговым импортом имен модулей друг в друга нужно быть очень осторожным, напрасно не усложняя код. Так, если бы в примере мы попытались импортировать имя var_2 из модуля file_2 в модуль file_3, а обратно, соответственно, имя var_3, интерпретатор сразу бы выдал нам ошибку. В тоже время транзитивный импорт прошел бы без проблем. Так мы могли бы импортировать имя var_1 из модуля file_1 сперва в модуль file_2, а затем из модуля file_2 в модуль file_3.

Также стоит добавить, что для импортирования модуля, который находится в том же каталоге подпакета, что и импортирующий модуль, относительный путь будет состоять всего лишь из одной точки. Например, для двух модулей mod_1 и mod_2, находящихся в одном подпакете package, инструкция относительного импорта второго модуля в первый будет иметь вид from . import mod_2.

Сокрытие данных в модулях

Как и в случае классов, сокрытие данных модулей в Python регулируется в основном на уровне соглашений, а не синтаксических конструкций. В основном это касается случаев, когда нужно уменьшить вероятность напрасного загрязнения пространства имен или случайного изменения значений переменных при импорте инструкциями from package import * (импорт всех доступных модулей пакета) и from module import * (импорт всех доступных имен модуля). Именно для этих случаев предусмотрено несколько основных соглашений для сокрытия данных:

  • Имена переменных, которые начинаются с одного символа нижнего подчеркивания, импортироваться инструкцией from module import * не будут.
  • Если на верхнем уровне модуля указать переменную __all__, присвоив ей список строк с именами переменных, то инструкция from module import * будет копировать только эти имена.
  • Если на верхнем уровне файла инициализации __init__.py пакета указать переменную __all__, присвоив ей список строк с именами модулей, то инструкция from package import * импортирует указанные в нем модули, хотя при пустом файле инициализации инструкция вообще ничего не делает (см. пример №13).

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

Код Результат pythonCodes
# Пусть в домашнем каталоге имеется пакет dir_1
# с модулями file_1.py, file_2.py и file_3.py. 

# Инструкции __init__.py в dir_1.   
# from dir_1 import * импортирует только file_3,
# модули file_1 и file_2 импортированы не будут.
# __all__ = ['file_3']

# Инструкции модуля file_1.    
# from dir_1.file_1 import * импортирует только 
# два указанных имени: var_1_1 и var_1_2.
# __all__ = ['var_1_1', 'var_1_2']
# var_1_1 = 1.1
# var_1_2 = 1.2
# var_1_3 = 1.3
                
# Инструкции модуля file_2.    
# var_2_1 = 2.1
# Для from dir_1.file_2 import * имя недоступно.
# _var_2_2 = 2.2
# Для from dir_1.file_2 import * имя недоступно.
# _var_2_3 = 2.3
                
# Инструкции модуля file_3.    
# var_3_1 = 3.1
               
# ----------------------------------------------------

# Импортируем из модулей все, что разрешено.
# Доступны имена: var_1_1 и var_1_2.
from dir_1.file_1 import *       
# Доступно только имя var_2_1.
from dir_1.file_2 import *         
        
# Из 1-го модуля. 
print('var_1_1:', var_1_1)
print('var_1_2:', var_1_2, end='\n\n')
# name 'var_1_3' is not defined.
# print('var_1_3:', var_1_3)

# Из 2-го модуля. 
print('var_2_1:', var_2_1, end='\n\n')
# name 'var_2_2' is not defined.
# print('var_2_2:', var_2_2)
# name 'var_2_3' is not defined.
# print('var_2_3:', var_2_3)

# ----------------------------------------------------    

# Будет импортирован модуль file_3, т.к. 
# он указан в списке __all__ в __init__.py.
from dir_1 import *   
    
# Смотрим, какие модули импортировались. 
# name 'file_1' is not defined.
# print('file_1:', file_1)
# name 'file_2' is not defined.
# print('file_2:', file_2)
# Все в порядке, модуль есть в __all__.
print('file_3:', file_3)
var_1_1: 1.1
var_1_2: 1.2

var_2_1: 2.1

file_3: <module 'dir_1.file_3' from 'D:\\python\\обучение\\dir_1\\file_3.py'>




















































		
			

Пример №13. Сокрытие данных в модулях.

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

Повторная загрузка модулей в Python

Как говорилось ранее, в ходе импорта программный код загружаемых модулей запускается всего лишь один раз за все время работы программы. При повторной попытке импорта того же модуля интерпретатор просто использует объект модуля, уже загруженный в память. Поэтому для принудительной повторной загрузки модуля и запуска его программного кода необходимо явно вызывать функцию reload(module), расположенную в модуле importlib стандартной библиотеки (см. пример №14).

Код Результат pythonCodes
# Пусть в каталоге основного модуля программы
# имеется пакет dir_1 с модулем file_1.

# Инструкции модуля file_1.
# print('importing file_1')

# Инструкции файла __init__.
# print('В файле __init__')    

# ---------------------------------------------------

# Импортируем из модуля станд. библиотеки функцию.
from importlib import reload

# Импортируем модуль впервые: __init__ выведет  
# 'В файле __init__', а file_1 - 'importing file_1'.
import dir_1.file_1    

# Во 2-й раз ничего не происходит, т.к. интерпретатор 
# обратился к уже загруженному объекту модуля.
import dir_1.file_1     

# Загружаем принудительно: инструкция в file_1
# опять сработала и вывела 'importing file_1', а
# вот файл __init__ 2-й раз не загрузился!
reload(dir_1.file_1)
В файле __init__
importing file_1
importing file_1





















		
			

Пример №14. Повторная загрузка модулей (часть 1).

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

Перечислим основные нюансы, на которые следует обращать внимание при использовании функции reload(module).

  • В ходе принудительного импорта инструкции файлов инициализации __init__.py повторно не выполняются.
  • Т.к. обращение к атрибутам импортированного инструкцией import модуля осуществляется посредством указания полного имени в формате module.name, то после перезагрузки измененного модуля обращение к тому же имени будет возвращать уже новое значение.
  • В результате перезагрузки ранее импортированного модуля происходит перезапись существующего пространства имен основного модуля. Поэтому всем ранее импортированным именам заново присваиваются значения, имеющиеся в дополнительном модуле на момент повторного импорта. При этом, если в измененном импортируемом модуле некоторые имена отсутствуют, то ранее импортированные имена в основном модуле остаются неизменными, т.к. пространство имен основного модуля именно перезаписывается, а не удаляется и заново создается (см. пример №15).
  • Поскольку инструкция from копирует объекты, а не ссылки, то изменения не коснутся переменных, импортированных этой инструкцией еще до принудительной перезагрузки дополнительного модуля. Они по-прежнему будут ссылаться на старые объекты, полученные до выполнения перезагрузки. Поэтому для получения новых значений, если это необходимо, потребуется повторное использование инструкции from.
Код Результат pythonCodes
# Пусть в каталоге основного модуля
# программы имеется модуль file_1.

# Инструкции модуля file_1.
# var_1 = 1
# var_2 = 2
# var_3 = 3    

# ---------------------------------------------------

# Импортируем из модуля станд. библиотеки функцию.
from importlib import reload
# Импортируем модуль впервые.
import file_1    
# Импортируем только 2 имени из модуля.
from file_1 import var_2, var_3   
    
# Выведет 1 2 3.   
print(file_1.var_1, file_1.var_2, file_1.var_3)
# Выведет 2 3.   
print(var_2, var_3, end='\n\n')    
   
# Изменяем значения после импорта.   
file_1.var_1 = 10
file_1.var_2 = 20    
var_2 = 20
var_3 = 30    
# Выведет 10 20 3.   
print(file_1.var_1, file_1.var_2, file_1.var_3)
# Выведет 20 30.
print(var_2, var_3, end='\n\n')     
    
# Перезаписываем модуль file_1, изменяя
# значение var_1 на 100 и удалив var_3.

# Открываем файл для записи.
f = open('file_1.py', 'w', encoding='utf-8')
# Полностью перезаписываем его.
f.write('var_1 = 100\nvar_2 = 2')
# Закрываем файловый объект.
f.close()

# Перезагружаем file_1 принудительно.
reload(file_1)      
    
# Выведет 100 2.   
print(file_1.var_1, file_1.var_2)
# Выведет 3, т.к. перезаписываются только 
# существующие имена в пространстве имен.   
print(file_1.var_3)    
# Выведет опять 20 30, т.к. нужен
# повторный импорт инструкцией from.   
print(var_2, var_3)
1 2 3
2 3

10 20 3
20 30

100 2
3
20 30










































		
			

Пример №15. Повторная загрузка модулей (часть 2).

Встроенная функция open создает объект файла, который обеспечивает связь с требуемым файлом, позволяя выполнять операции чтения и записи с помощью методов полученного объекта. Подробнее эту функцию мы разберем чуть позже. А сейчас постарайтесь хотя бы бегло ознакомиться с ней в справочнике стандартной библиотеки в разделе Built-in Functions.

Важно добавить, что при повторной загрузке модуля, интерпретатор перезагружает только данный конкретный файл модуля, не затрагивая модули, которые были импортированы самим перезагружаемым модулем. Это связано с тем, что инструкции импорта этих дополнительных модулей просто вернут их объекты, которые были загружены ранее. Поэтому для пущей уверенности в транзитивной перезагрузке требуемых модулей, лучше вызывать функцию reload в каждом таком модуле для непосредственного обновления субкомпонентов (см. пример №16).

Код Результат pythonCodes
# Пусть в каталоге основного модуля имеется модуль
# file_0, а также пакет dir_1 с модулем file_1.
    
# Инструкции модуля file_0.
# from importlib import reload    
# import dir_1.file_1 as f_1
        
# Инструкции модуля file_1.
# var_1 = 1

# ---------------------------------------------------

# Импортируем из модуля станд. библиотеки функцию.
from importlib import reload
# Импортируем модуль впервые.
import file_0   
        
# Выведет 1.   
print(file_0.f_1.var_1, end='\n\n')
  
# Изменяем значения после импорта.   
file_0.f_1.var_1 = 10
# Выведет 10.   
print(file_0.f_1.var_1, end='\n\n') 

# Перезаписываем модуль file_1.
# Открываем файл для записи.
f = open('dir_1/file_1.py', 'w', encoding='utf-8')
# Полностью перезаписываем его.
f.write('var_1 = 100')
# Закрываем файловый объект.
f.close()

# Перезагружаем file_0 принудительно.
reload(file_0)      
# Выведет все равно 10.   
print(file_0.f_1.var_1, end='\n\n')
          
# Пробуем перезагрузить и file_1 принудительно.
file_0.reload(file_0.f_1)     
# Вот теперь выведет 100.   
print(file_0.f_1.var_1)
1

10

10

100

































		
			

Пример №16. Транзитивная перезагрузка модулей.

Импорт по имени в виде строки в Python

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

Начнем со встроенной функции exec(), которая может принимать строку с инструкцией и компилировать ее в код с последующей передачей его интерпретатору (см. пример №17).

Код Результат pythonCodes
# Используем для импорта функцию exec().
exec('from dir_1.file_1 import var_2')
exec('import dir_1.file_1 as f_1')   
        
# Выведет 1.   
print(f_1.var_1)
# Выведет 2.   
print(var_2)
1
2




		
			

Пример №17. Использование для импорта функции exec.

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

Импортировать нужный модуль, пакет или отдельное имя можно также при помощи встроенной функции __import__(name, globals=None, locals=None, fromlist=(), level=0), принимающей в качестве аргумента имя модуля в виде строки и возвращающей объект импортированного модуля, который затем желательно сохранить в переменной для дальнейшего использования (см. пример №18).

Код Результат pythonCodes
# Пусть в каталоге основного модуля имеется модуль
# file_0 и пакет dir_1 с модулем file_1.
         
# Инструкции модуля file_0.
# var_0 = 0
         
# Инструкции модуля file_1.
# var_1 = 1      
# var_2 = 2    

# ---------------------------------------------------

# Сохраняется объект модуля file_0.
f = __import__('file_0')
# <module 'file_0' from ...   
print(f)    
# Выведет 0.   
print(f.var_0, end='\n\n')

# Сохраняется объект пакета верхнего уровня, т.е. dir_1,
# со всеми вложенными объектами цепочки, т.е. с file_1.
f = __import__('dir_1.file_1')
# <module 'dir_1' from ...   
print(f)    
# <module 'dir_1.file_1' from ...   
print(f.file_1)
# Выведет 1 2.   
print(f.file_1.var_1, f.file_1.var_2, end='\n\n')    

# Если хотим сохранить объект модуля, нужно указать
# его в виде строки в списке аргумента fromlist.
# Реализация import dir_1.file_1 as f.
f = __import__('dir_1.file_1', fromlist=['file_1'])
# <module 'dir_1.file_1' from ...   
print(f)    
# Выведет 1 2.   
print(f.var_1, f.var_2, end='\n\n')    
    
# Реализация from dir_1.file_1 import var_1, var_2 as v_2.
f = __import__('dir_1.file_1', fromlist=['file_1'])
var_1 = f.var_1
v_2 = f.var_2    
# Сам объект модуля можно удалить.
del f
# Выведет 1 2.   
print(var_1, v_2)
<module 'file_0' from 'D:\\python\\обучение\\file_0.py'>
0

<module 'dir_1' from 'D:\\python\\обучение\\dir_1\\__init__.py'>
<module 'dir_1.file_1' from 'D:\\python\\обучение\\dir_1\\file_1.py'>
1 2

<module 'dir_1.file_1' from 'D:\\python\\обучение\\dir_1\\file_1.py'>
1 2

1 2

































		
			

Пример №18. Импорт с помощью функции __import__.

Таким образом, для импорта модуля, который находится в том же каталоге, что и импортирующий модуль, достаточно передать функции имя модуля в виде строки и затем сохранить его в переменной для дальнейшего использования. Если же модуль импортируется из пакета, нужно передавать строку с путем к нему, отделяя подпакеты точкой, как и в случае с обычной инструкцией импорта. Однако нужно иметь в виду, что в этом случае функция __import__ возвращает объект пакета верхнего уровня, а не сам модуль, как мы привыкли видеть при обычном импорте. Для получения объекта самого модуля нужно либо получить его в виде атрибута, либо просто указать его имя в списке аргумента fromlist=['module'] при вызове функции. Если нужен не весь модуль, а только некоторые имена, можно извлечь их из сохраненного объекта модуля, сохранив под нужными пседонимами в переменных, а сам объект модуля удалить (еще раз посмотрите пример).

Следует отметить, что использование функции __import__ вне интерпретатора не рекомендуется. Вместо этого в пакете importlib стандартной библиотеки имеется функция import_module(name, package=None), которая также принимает путь к файлу модуля в виде строки, но возвращает именно тот объект пакета или модуля, который мы ожидаем получить, а не объект пакета верхнего уровня. Более того, если передать аргументу package имя пакета для отсчета, то путь name можно будет передать и в относительном виде (см. пример №19).

Код Результат pythonCodes
# Пусть в каталоге основного модуля имеется модуль
# file_0 и пакет dir_1 с модулем file_1.
         
# Инструкции модуля file_0.
# var_0 = 0
         
# Инструкции модуля file_1.
# var_1 = 1      
# var_2 = 2    

# ---------------------------------------------------

# Импортируем из модуля функцию.
from importlib import import_module

# Сохраняется объект модуля file_0.
f = import_module('file_0')
# <module 'file_0' from ...   
print(f)    
# Выведет 0.   
print(f.var_0, end='\n\n')

# Сохраняется объект модуля file_1.
f = import_module('dir_1.file_1')
# <module 'file_1' from ...   
print(f)
# Выведет 1 2.   
print(f.var_1, f.var_2, end='\n\n')    
        
# Используем относительный путь, начиная от dir_1.
f = import_module('.file_1', 'dir_1')
# <module 'file_1' from ...   
print(f)
# Выведет 1 2.   
print(f.var_1, f.var_2, end='\n\n')        
        
# Реализация from dir_1.file_1 import var_1, var_2 as v_2.
f = import_module('dir_1.file_1')
var_1 = f.var_1
v_2 = f.var_2    
# Сам объект модуля можно удалить.
del f
# Выведет 1 2.   
print(var_1, v_2) 
<module 'file_0' from 'D:\\python\\обучение\\file_0.py'>
0

<module 'dir_1.file_1' from 'D:\\python\\обучение\\dir_1\\file_1.py'>
1 2

<module 'dir_1.file_1' from 'D:\\python\\обучение\\dir_1\\file_1.py'>
1 2

1 2

































		
			

Пример №19. Импорт с помощью функции import_module.

Старайтесь использовать для динамического импорта требуемых пакетов и модулей именно функцию import_module(name, package=None), а не встроенную функцию __import__.

Специальные атрибуты модулей в Python

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

  • __doc__ – содержит строку документирования модуля;
  • __file__ – содержит полный путь к файлу, из которого модуль был создан либо загружен;
  • __name__ – содержит полное имя модуля.

Последний атрибут представляет особый интерес, т.к. на его основе реализуется простейший специальный прием, позволяющий либо импортировать файлы как модули, либо запускать их как самостоятельные программы (см. пример №20).

Код Результат pythonCodes
'''Модуль main.py, версия 1.0'''

# Центральная функция программы.
def main():
              
    # Выводим описание модуля.    
    print('Описание: «{}».'.format(__doc__))
    # Выводим путь к модулю.    
    print('Путь к файлу: «{}».'.format(__file__))    
    # Выводим имя модуля.    
    print('Модуль: «{}».'.format(__name__))    
            
    # Запрос ввода, чтобы программа не закрылась.
    input()
    
# Если модуль не импортируется, а 
# запускается как основная программа.
if __name__ == '__main__':
    # Вызываем основную функцию программы.
    main()
Описание: «Модуль main.py, версия 1.0».
Путь к файлу: «D:\python\обучение\main.py».
Модуль: «__main__».















		
			

Пример №20. Специальные атрибуты модулей.

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

  • если модуль запускается как основной файл программы, атрибуту присваивается строка '__main__';
  • если же файл импортируется, атрибуту присваивается строка с именем модуля (имя файла без расширения), под которым он будет известен клиенту.

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

Краткие итоги параграфа

  • Модуль – это отдельный файл с программным кодом на языке Python, который создается один раз и далее может быть использован программами многократно. Для формирования модуля необходимо создать обычный текстовый файл с расширением *py и записать в него целевые программные инструкции.
  • Подключение дополнительных модулей к основному модулю (импорт модулей) осуществляется инструкциями import module и from module import name в различных их вариациях.
  • В отличие от инструкции import использовать инструкцию from ... * в локальной области видимости функций запрещается, в то время как остальные вариации этой инструкции вполне допустимы. Но в любом случае импорт в локальных областях видимости должен быть обоснованным, чтобы зря не повторять ресурсоемкую операцию импорта, запускаемую, например, при каждом новом вызове функции.
  • Для длинных импортируемых имен разрешается использовать псевдонимы, устанавливаемые расширением as инструкций импорта в форматах import long_module_name as name и from module import long_name as name, а также допустимых их вариаций.
  • Пакет – это отдельный каталог в Python, в котором содержатся модули и другие пакеты, а также обязательный файл __init.py__, отвечающий за инициализацию пакета. Важно помнить, что в инструкции импорта путь к требуемому модулю пакета нужно указывать, используя для разделения обычные точки (например, import dir_1.dir_2.file).
  • Согласно принятым рекомендациям первыми должны импортироваться модули и пакеты стандартной библиотеки, затем модули и пакеты сторонних библиотек и лишь в конце пользовательские модули и пакеты.
  • В случае импортирования подпакетов и модулей внутри пакетов для инструкции from <path> import <name> имеется возможность использовать относительный путь вместо абсолютного. В этом случае для указания текущего пакета, в котором находится модуль осуществляющий операцию импорта, используется одна ведущая точка, а для указания подпакета, расположенного на один уровень вверх, используются две ведущие точки. Соответственно, три ведущие точки означают подпакет на два уровня выше и т.д. Главное, чтобы все каталоги являлись подпакетами, т.е. находились внутри корневой папки всего пакета, и содержали файл инициализации __init.py__.
  • Python выполняет поиск модулей и пакетов не хаотически, а в соответствии с установленным порядком: сперва проверяется наличие импортируемого имени во встроенном модуле, после чего поочередно проверяются каталоги, список которых содержится в переменной sys.path. Первым в этом списке идет домашний каталог программы, а также присутствует каталог с модулями стандартной библиотеки. Они всегда автоматически включаются в путь поиска интерпретатора, поэтому до тех пор, пока мы размещаем все наши дополнительные модули и пакеты программы в ее корневой папке, нам не нужно беспокоится о каких-либо настройках пути поиска интерпретатора. При этом следует помнить, что не нужно называть собственные модули или пакеты аналогично поставляемым с дистрибутивом Python, т.к. интерпретатор обнаружит пользовательские модули первыми и даже не будет пытаться продолжать поиск в каталоге стандартной библиотеки.
  • В процессе первичного импорта выполняются все инструкции файлов инициализации и модулей. При повторной попытке импортированных ранее модулей интерпретатор просто использует объекты модулей, уже загруженные в память. Поэтому для принудительной повторной загрузки модуля и запуска его программного кода необходимо явно вызывать функцию reload(module), расположенную в модуле importlib стандартной библиотеки. При этом следует учитывать основные нюансы, на которые следует обращать внимание при использовании функции reload.
    • В ходе принудительного импорта инструкции файлов инициализации __init__.py повторно не выполняются.
    • Т.к. обращение к атрибутам импортированного инструкцией import модуля осуществляется посредством указания полного имени в формате module.name, то после перезагрузки измененного модуля обращение к тому же имени будет возвращать уже новое значение.
    • В результате перезагрузки ранее импортированного модуля происходит перезапись существующего пространства имен основного модуля. Поэтому всем ранее импортированным именам заново присваиваются значения, имеющиеся в дополнительном модуле на момент повторного импорта. При этом, если в измененном импортируемом модуле некоторые имена отсутствуют, то ранее импортированные имена в основном модуле остаются неизменными, т.к. пространство имен основного модуля именно перезаписывается, а не удаляется и заново создается.
    • Поскольку инструкция from копирует объекты, а не ссылки, то изменения не коснутся переменных, импортированных этой инструкцией еще до принудительной перезагрузки дополнительного модуля. Они по-прежнему будут ссылаться на старые объекты, полученные до выполнения перезагрузки. Поэтому для получения новых значений, если это необходимо, потребуется повторное использование инструкции from.
  • Как и в случае классов, сокрытие данных модулей в Python регулируется в основном на уровне соглашений, а не синтаксических конструкций.
    • Имена переменных, которые начинаются с одного символа нижнего подчеркивания, импортироваться инструкцией from module import * не будут.
    • Если на верхнем уровне модуля указать переменную __all__, присвоив ей список строк с именами переменных, то инструкция from module import * будет копировать только эти имена.
    • Если на верхнем уровне файла инициализации __init__.py пакета указать переменную __all__, присвоив ей список строк с именами модулей, то инструкция from package import * импортирует указанные в нем модули, хотя при пустом файле инициализации инструкция вообще ничего не делает.
  • В Python предусмотрен ряд инструментов, позволяющих работать с именами импортируемых модулей в виде строки. Сюда относятся функции exec, __import__ и import_module. Первая функция принимает строку с инструкцией и компилирует ее в код, а две оставшиеся принимают имя модуля в виде строки и возвращающей объект импортированного модуля. Вне интерпретатора рекомендуется использовать функцию import_module(name, package=None) из пакета importlib стандартной библиотеки.
  • Для того, чтобы проверить как используется модуль, т.е. в виде самостоятельной программы или же импортируется, можно использовать условную инструкцию if __name__ == '__main__':. Если модуль запускается как основной файл программы, атрибуту __name__ будет присвоена строка '__main__' (условие будет истинным), а если файл импортируется, атрибуту будет присвоена строка с именем модуля (условие будет ложным).

Вопросы и задания для самоконтроля

1. Как создать модуль? Показать решение.

Ответ. Для создания модуля необходимо создать обычный текстовый файл (обычно с расширением *py) и записать в него целевые программные инструкции на языке Python. Какие-то специальные синтаксические конструкции для этого использовать не нужно.

2. Назовите 4 основные категории, на которые делятся модули в Python. Показать решение.

Ответ. Встроенные модули и модули стандартной библиотеки (входят в состав дистрибутива языка), сторонние модули (более 90 000 модулей и пакетов, которые могут быть установлены из каталога пакетов на официальном сайте с помощью утилиты pip), пользовательские модули (их мы пишем сами).

3. В каталоге с главным файлом программы имеется модуль mod.py. Как импортировать этот модуль в основной файл программы? А как импортировать все имена из него? Показать решение.

Ответ. В первом случае нужно использовать инструкцию import mod, а во втором – инструкцию from mod import *.

4. В каком порядке желательно группировать инструкции импорта модулей? Показать решение.

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

5. Как внутри основного (импортирующего) модуля получить доступ к переменной var в допол­нительном модуле mod, который ранее был импортирован инструкцией import? Показать решение.

Ответ. Необходимо использовать точечную нотацию mod.var, т.к. все глобальные переменные импортированного модуля автоматически становятся атрибутами объекта mod этого модуля в пространстве имен импортирующего модуля.

6. Исправьте в коде все ошибки, чтобы он заработал. Показать решение.

Условие pythonCodes
# Импортируем функцию sqrt.
import sqrt from mmath   

# Выводим корень из числа на экран.
print(sqrt(169))

Ответ. Данная вариация инструкции импорта должна иметь формат from module import name.

Решение Результат pythonCodes
# Импортируем функцию sqrt.
from math import sqrt   

# Выводим корень из числа на экран.
print(sqrt(169))
13.0



			

7. Как использовать расширение as в инструкциях импорта? Показать решение.

Ответ. Инструкцию as удобно использовать для создания псевдонимов при импорте очень длинных имен либо имен, которые уже присутствуют в целевом модуле. Основные форматы использования этой инструкции: import long_module_name as name и from module import very_long_name as name.

8. В каком случае вместо инструкции from лучше использовать инструкцию import? Показать решение.

Ответ. В случае, когда необходимо обеспечить доступ к одному и тому же имени name в некотором модуле mod сразу из нескольких импортирующих его модулей. Ведь импорт модуля mod инструкцией import, дает возможность совместно модифицировать или заменять значение целевой переменной name из любого импортирующего модуля посредством обращения к ней через mod.name.

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

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

10. Как создать пакет модулей? Показать решение.

Ответ. Для создания структуры пакета необходимо создать каталог с подкаталогами и в каждом из них расположить обязательный специальный файл инициализации __init.py__, инструкции которого будут выполняться каждый раз при первичном импорте пакета или подпакета, а также файлы с python-кодом, которые будут представлять собой модули пакета и его подпакетов (их инструкции, кстати, тоже выполняются при первичном импорте).

11. Что означают две точки в относительном пути импорта инструкции from? Три точки? Можно ли использовать указание относительного пути в инструкции import? Показать решение.

Ответ. Две точки означают каталог на один уровень выше текущего, три – на два уровня выше. Использовать указание относительного пути разрешается только в инструкции from внутри подпакетов.

12. Как импортировать имя name из модуля mod подкаталога импортирующего модуля, используя относительный путь? Показать решение.

Ответ. В этом случае подойдет инструкция from .mod import name.

13. Чем инструкция from package import name отличается от инструкции from . import name? Показать решение.

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

14. Назовите два способа запретить копирование имен инструкцией from * из программного кода верхнего уровня импортируемого модуля. Показать решение.

Ответ. Согласно основным соглашениям о сокрытии данных имеем:

  • имена переменных, которые начинаются с одного символа нижнего подчеркивания, импортироваться инструкцией from module import * не будут;
  • если на верхнем уровне модуля указать переменную __all__, присвоив ей список строк с именами переменных, то инструкция from module import * будет копировать только эти имена.

15. Для чего предназначена функция reload? Эта функция встроенная или перед использованием ее нужно импортировать? Показать решение.

Ответ. Функция reload(module) используется для принудительной повторной загрузки модуля и запуска его программного кода. Расположена эта функция в модуле importlib стандартной библиотеки, а значит перед использованием она должна быть сперва импортирована из нее, например, инструкцией from importlib import reload.

16. Что будет выведено на экран пользователя в результате последних четырех вызовов функции print? Поясните свой ответ. Показать решение.

Условие pythonCodes
# Импортируем функцию reload.
from importlib import reload
# Импортируем весь модуль.
import file_1    
# Импортируем только одно имя.
from file_1 import var_1     

# Везде вывело 10.   
print(file_1.var_1)
print(var_1)    

# Открываем файл для записи.
f = open('file_1.py', 'w', encoding='utf-8')
# Полностью перезаписываем его.
f.write('var_1 = 100')
# Закрываем файловый объект.
f.close()
       
# Что будет выведено теперь?   
print(file_1.var_1)    
print(var_1)     

# Перезагружаем file_1.
reload(file_1)      
    
# А что будет выведено здесь?   
print(file_1.var_1)
print(var_1)

Ответ. В ходе первых двух вызовов функции print будет выведено прежнее значение 10, т.к. изменения в импортированном файле затронут главный модуль только после принудительной перезагрузки функцией reload. Именно поэтому в результате третьего вызова будет выведено измененное значение, т.е. 100. В случае повторного импорта с помощью инструкции import, мы изменений также не наблюдали бы, т.к. интерпретатор просто использовал бы ранее загруженный объект модуля. Что касается четвертого вызова, то здесь по-прежнему будет выведено старое значение 10, т.к. даже принудительная перезагрузка никак не повлияет на копию объекта, созданную инструкцией from. Для изменения нужен повторный импорт этой инструкцией.

17. Как можно импортировать модуль, имя которого пользователь вводит в ответ на запрос программы? Показать решение.

Ответ. Поскольку имя вводится в виде строки, импорт можно осуществить при помощи встроенной функции __import__ либо функции import_module из пакета importlib стандартной библиотеки, что более предпочтительно в данном случае. А вот использование встроенной функции exec настоятельно не рекомендуется, т.к. она может стать источником злонамеренного кода пользователя!!!

18. Что можно сказать о текущем модуле, если переменная __name__ имеет значение '__main__'? Показать решение.

Ответ. Если переменная __name__ текущего модуля имеет значение '__main__', то он выполняется как самостоятельный сценарий (используется как программа), а не был импортирован другим модулем программы (используется в качестве библиотеки).

19. Дополнительные тесты по теме расположены в разделе «Импорт, модули и пакеты» нашего сборника тестов по основам Питона.

20. Дополнительные упражнения и задачи по теме расположены в разделе «Импорт, модули и пакеты» нашего сборника задач и упражнений по языку программирования Python.

Быстрый переход к другим страницам