Работа с модулями

Имена модулей - должны соотвествовать тем же правилам, что любые идентификаторы (алфавитные символы, цифры, подчеркивание; не ключевые слова).

То есть пробел в имени файла не допустим, даже если ОС это допускает.

Использование модулей

Пусть есть файл b.py:

def spam(text):
    print(text, 'spam')

Если мы хотим импортировать этот файл и вызвать функцию smap, то можем:

import вызов что значит
import b b.spam('a') b - имя переменной, которая ссылается на объект модуля после его загрузки
from b import spam spam('a') импорт модуля + копирование имени spam в пространство имен
from b import * spam('a') импорт модуля + копирование всех имен верхнего уровня (вне def или class)

from .. * в Python3 может использоваться только на верхнем уровне.

as - псевдонимы модулей

Иногда у модуля слишком длинное имя или мы указываем имя модуля с пакетами. Тогда вместе с полным именем можно указать псевдоним, заданный с помощью as

import numpy as np
import matplotlib.pyplot as plt

Инструкции import и from - это операции присваивания

import и from - выполняемые инструкции, а не "объявления времени компиляции".

  • можно вложить в if (импортируем один модуль, иначе импортируем другой)
  • можно вложить в def (внутри функции)
  • работают только тогда, когда до них дойдет интерпретатор.

Т.е. имена и модули не доступны, пока не были выполнены нужные import.

Операция присваивания, те.

  • import присваивает объект модуля единственному имени;
  • from присваивает одно или более имен объектам с теми же именами в другом модуле. (т.е становятся ссылками на shared объекты)

b.py:

x = 1
y = [1, 2]

a.py:

from b import x, y  # скопировать два имени

x = 42              # изменяется только локальная переменная х
y[0] = 42           # изменяется объект по ссылке y

print(x, y)         # 42, [42, 2]

Хотим изменить x в модуле b.py.

  • Это очень плохо, потому что потом не найдешь кто где менял эту х.
  • Мы не контролируем изменения.
  • Инкапсулируйте изменения через вызов функции-обертки в модуле b.py

Но очень хочется:

>>> from small import x, y  # Скопировать два имени
>>> x = 42                  # Изменить только локальное имя x
>>> import small            # Получить имя модуля
>>> small.x = 42            # Изменить x в другом модуле

Что лучше - import или from?

модуль.атрибут

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

Рецепт (рекомендательный):

  • import - предпочтительнее;
  • from .. import - явно перечисляем имена;
  • from .. import * - только при одном таком импорте.

Пространство имен модуля

Пространство имен - место, где создаются имена.

Атрибуты модуля - имена, которые находятся в модуле.

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

Если в файле b.py на верхнем уровне пишем x = 1, то имя х - это атрибут модуля b.

x - глобальная переменная для кода внутри b.py

К х извне можно доступиться по имени b.x

  • Инструкции модуля выполняются при первом import.
    • создается объект модуля;
    • инструкции в модуле выполняются одна за другой.
  • Присваивание на верхнем уровне создают атрибуты модуля (имена сохраняются в пространстве имен модуля):
    • =
    • def
    • class
  • доступ к простанству имен модуля можно получить через b.__dict__ или dir(b):
    • __dict__ - все, что есть в пространстве - для работы
    • dir - вместе с унаследованными, не полное, отсортированное - для просмотра.
  • модуль - единая область видимости (глобальная).

Файл module2.py:

Print('starting to load...')
import sys
name = 42
def func(): pass
class klass: pass
print('done loading.')

Импортируем его в интерпретаторе:

>>> import module2
starting to load...
done loading.

То есть код файла b.py действительно выполняется.

Область видимости модуля после импортирования превратилась в пространство имен атрибутов объекта модуля и они доступны по модуль.атрибут:

>>> module2.sys
<module 'sys' (built-in)>
>>> module2.name
42
>>> module2.func
<function func at 0x026D3BB8>>
>>> module2.klass
<class module2.klass>

Внутри интерпретатора пространство имен хранится в виде обычного словаря. Разные пространства имен - разные словари.

>>> list(module2.__dict__.keys())
['name', '__builtins__', '__file__', '__package__', 'sys', 'klass', 'func',
'__name__', '__doc__']
  • __file__ - имя файла, из которого был загружен модуль;
  • __name__ - имя модуля (без диреторий и расширения файла).

Квалификация имен атрибутов

Это доступ по объект.атрибут

  • x - простая переменная - ищется в текущих областях видимости по правилу LEGB.
  • x.y - квалифицированное имя - ищем х в текущих областях видимости, далее ищем атрибут y в объекте х (а не областях видимости)
  • x.y.z - квалифицированные пути - ищем сначала имя y в объекте х, потом имя z в объекте x.y

Квалификация имен применяется ко всем объектам, имеющим атрибуты (модули, классы, расширения на языке С и тд.)

Импортирование и область видимости

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

Файл b.py:

x = 88          # x глобальна только для этого файла
def f():
    global x    # будем изменять x в этом файле
    x = 99      # имена в других модулях недоступны

Файл a.py:

x = 11          # x глобальна только для этого файла
import b        # получаем доступ к именам в модуле b
b.f()           # изменяет переменную b.x, но не х этого файла
print(x, b.x)   # 11 99

Запустим a.py, получим 11 99.

Когда вызвали b.f(), ее глобальной областью видимости будет тот файл, где она написана, а не там, откуда вызвана.

Вложенные пространства имен

Операция импорта не дает возможности из модуля b доступиться к переменным модуля а (к внешней области видимости).

Но можно доступиться к вложенным областям видимости.

# c.py:
x = 3
# b.py:
x = 2
import c
print(x, c.x)        # 2 3
# a.py:
x = 1
import b
print(x, b.x, b.c.x) # 1 2 3

Запустим файл a.py:

2 3
1 2 3

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

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

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

Обновление сервисов, которые нельзя останавливать.

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

Отличия reload от import:

  • Функция, а не инструкция.
  • ей передается объект модуля, а не имя.
  • не забудьте import imp
import module               # Первоначальное импортирование
что-то делаем, используя этот модуль
from imp import reload      # Импортировать функцию reload (в 3.0)
reload(module)              # Загрузить обновленный модуль
делаем что-то дальше, используя обновленные атрибуты модуля
  • Функция reload запускает новый программный код в файле модуля в теку- щем пространстве имен модуля. При повторном выполнении программный код перезаписывает существующее пространство имен вместо того, чтобы удалять его и создавать вновь.
  • Инструкции присваивания на верхнем уровне файла замещают имена но- выми значениями. Например, повторный запуск инструкции def приводит к замещению предыдущей версии функции в пространстве имен модуля, выполняя повторную операцию присваивания имени функции.
  • Повторная загрузка оказывает воздействие на всех клиентов, использо- вавших инструкцию import для получения доступа к модулю. Клиенты, использовавшие инструкцию import, получают доступ к атрибутам модуля, указывая полные их имена, поэтому после повторной загрузки они будут получать новые значения атрибутов.
  • Повторная загрузка будет воздействовать лишь на тех клиентов, которые еще только будут использовать инструкцию from в будущем. Клиенты, ко- торые использовали инструкцию from для получения доступа к атрибутам в прошлом, не заметят изменений, произошедших в результате повторной загрузки, – они по-прежнему будут ссылаться на старые объекты, получен- ные до выполнения перезагрузки.

results matching ""

    No results matching ""