0 знаков
60. Еще о возможностях модулей в Python
Кратко- В Python любой код всегда находится внутри модуля.
- Сведите к минимуму количество глобальных переменных.
- Модули должны в редких случаях менять переменные других модулей.
- Лучше обмениваться данными между модулями при помощи аргументов функций и возвращаемых значений.
- Сокрытие данных в модулях: использование нижнего подчеркивания для приватных переменных.
- Включение будущих средств языка: оператор
import
с необязательными расширениями.- Переменная
__name__
в Python: используется для определения, является ли файл модулем или сценарием.- Аргументы командной строки в Python: модуль
sys
для работы с ними.- Изменение пути поиска модулей в Python: использование функции
exec()
для динамического выполнения кода.
Введение
В предыдущем уроке мы познакомились с пакетами в Python, еще ранее научились писать модули. Осталось поговорить об оставшихся темах, которые не вошли в предыдущие уроки: переменная __name__
, модуль __future__
, сокрытие данных и другие.
Для начала рассмотрим основные принципы и идеи, связанные с модулями.
В Python любой код всегда находится внутри модуля, даже набираемый в интерактивной оболочке, на самом деле оказывается во встроенном модуле __main__
.
Сведите к минимуму количество глобальный переменных. Про инкапсуляцию поговорим в теме по ООП.
Модули должны в редких случаях менять переменные других модулей. Использование глобальных переменных является частой практикой для передачи результатов между модулями. Зачастую такое решение следует из-за неправильного проектирования (за исключением некоторых случаев) и приводит к запутыванию кода и усложнению его дальнейшей поддержки. Лучше обмениваться данными между модулями при помощи аргументов функций и возвращаемых значений.
Сокрытие данных в модулях
Иногда нам необходимо ограничить доступ к некоторым данным извне модуля. В этом уроке «сокрытие данных» и «инкапсуляция» будем считать синонимами. Сразу приведу пример, а дальше разберемся:
''' b.py '''
a = 1
_b = 2
И основной файл:
''' a.py '''
from b import *
print(a)
print(_b)
В Python все имена модуля являются публичными, т.е. доступными за пределами модуля. Но, если имя начинается с нижнего подчеркивания (как переменная _b
), то оно становится приватным, т.е. недоступным за пределами модуля. Попробуйте запустить вышеуказанный код и убедиться в этом.
Такой способ сделать имя приватным работает только с оператором from … *
. Давайте перепишем пример с использованием обычного импортирования при помощи import
.
''' a.py '''
import b
print(b.a)
print(b._b)
Как видите, переменная _b
не такая уже и приватная на самом деле. Взглянем на другой (противоположный) способ: переменная __all__
. Python сначала ищет в модуле переменную __all__
, содержащую список с именами (независимо, есть ли перед ними подчеркивание) и делает их все публичными, т.е. оператор from … *
копирует их в файл верхнего уровня, а все остальные имена становятся «приватными».
''' b.py '''
__all__= ['_b']
a = 1
_b = 2
Файл верхнего уровня:
''' a.py '''
from b import *
print(a)
print(_b)
При использовании обычного импортирования при помощи оператора import
, мнимая приватность не работает.
Включение будущих средств языка: __future__
Изменение языка иногда может нарушить работу кода и поэтому в Python решили вводить такие изменения постепенно в виде необязательных расширений, которые выключены по умолчанию. Чтобы их включить, необходимо использовать оператор import
следующего вида:
from __future__ import название
Переменная __name__
в Python
У вас может быть какой-то сценарий со своими функциями и работающий как файл верхнего уровня. Бывают случаи, когда из этого сценария нужны какие-то функции и поэтому его нужно подключить как модуль. Лучше сразу рассмотреть пример:
''' a.py '''
def print_x(x):
print(x)
def main():
print('Выполнение сценария началось')
print_x(10)
if __name__ == '__main__':
main()
Если вы программировали на С/С++, то «упаковка» кода внутрь функции main()
вас не удивит. Запустите этот код и убедитесь в его очевидной работе. Он работает точно так же, как и этот:
''' a.py '''
def print_x(x):
print(x)
print('Выполнение сценария началось')
print_x(10)
Разница будет видна, когда вы будете использовать файл a.py
в роли модуля. Теперь создадим второй файл, который будет выступать в роли файла верхнего уровня:
''' b.py '''
import a
a.print_x(12)
Попробуйте запустить этот скрипт с двумя вариантами файла a.py
по очереди. Когда файл a.py
используется как модуль, переменная __name__
устанавливается в false
и функция main()
не выполняется. Как вы догадались, первый пример файла a.py
можно было записать следующим образом:
''' a.py '''
def print_x(x):
print(x)
if __name__ == '__main__':
print('Выполнение сценария началось')
print_x(10)
Если вы забыли, как работают функции, то можете повторить раздел «Функции», начиная с урока «Основы функций в Python».
Аргументы командной строки
Если вы знакомы с С/С++, то вероятно знаете про аргументы командной строки:
int main(int argc, char* argv[]) { /* ... */ }
В Python тоже есть аргументы командной строки, но они не объявляются так явно в коде. Рассмотрим пример:
''' a.py '''
import sys
if __name__ == '__main__':
print(sys.argv)
Во-первых, следует подключить модуль sys
. Этот модуль имеет список аргументов argv
, в котором хранится первым элементом путь до запускаемого скрипта, а далее переданные через командную строку аргументы:
a.py 123 Слово
Скрипт a.py
напечатает в консоли:
['a.py', '123', 'Слово']
Как перебирать списки вы научились в уроке «Списки в Python».
Изменение пути поиска модулей
В первом уроке по модулям «Модули в Python» мы говорили о возможности изменения путей поиска, но не показывали, как это сделать. Напомню, по итогу образуется список из путей, который хранится под именем path
в модуле sys
. Чтобы посмотреть список путей, в которых осуществляется поиск, можно просто вывести этот список:
''' a.py '''
import sys
print(sys.path)
Мы уже несколько раз сказали: «Это список». Этот список мы можем редактировать как нам вздумается: добавлять элементы, удалять их или полностью очистить список. О методах работы со списками мы говорили в уроке «Списки в Python». Например, добавим путь:
sys.path.append('c:\code')
Выполнение строк с кодом
Если вдруг вам необходимо импортировать модуль, название которого «создается» динамически, можете попробовать так:
import 'module_1'
И получить ошибку о неправильном синтаксисе. Хорошо. Попробуем обмануть и укажем динамически созданное название модуля в переменной:
x = 'module_1'
import x
Синтаксической ошибки не будет, но и модуль module_1
не будет подключен, так как интерпретатор будет искать с именем x
. Переменная будет восприниматься как название модуля. Не расстраивайтесь, мы можем это обойти при помощи функции exec()
, которая динамически выполняет большие блоки Python-кода.
x = 'module_1'
exec('import ' + x)
Разумеется, это не единственное применение функции exec()
. Функция exec()
и похожая на нее eval()
(выполняет строку кода) таят в себе множество опасностей, особенно, если в эти функции передаются данные из недостоверного источника. В интернете полно статей на эту тему и еще больше споров о возможности сделать эти функции безопасными для применения.
Что касается предварительной компиляции динамически созданного модуля, мы можем использовать вместо exec()
встроенную функцию __import__
, возвращающую объект модуля.
x = 'module_1'
__import__(x)
На этом закончим изучение модулей. В этом уроке вы узнали о способах сокрытия данных в модулях при помощи использования в имени нижнего подчеркивания или явного указания переменной __all__
. Научились включать будущие языковые средства для тестирования своего кода. Узнали о переменной __name__
, которая позволяет использовать файл верхнего уровня в роли модуля и попробовали передать аргументы в сценарий из командной строки. В конце урока научились выполнять строки с кодом и подключать модули с динамическим именем. В следующем уроке подведем итоги раздела и затем перейдем к ООП.
Тест
Похожие уроки Codebra
Подписывайся на наш Telegram-канал!
Новости, полезный материал,
программирование и ИБ
Подписывайся на наш Telegram-канал!
Новости, полезный материал,
программирование и ИБ