Главная страница
Навигация по странице:

  • Функция reload не обладает транзитивностью

  • Рекурсивный импорт с инструкцией from может не работать

  • Закрепление пройденного Контрольные вопросы

  • Упражнения к пятой части

  • Математический анализ. 3е издание


    Скачать 4.86 Mb.
    Название3е издание
    АнкорМатематический анализ
    Дата04.02.2022
    Размер4.86 Mb.
    Формат файлаpdf
    Имя файлаpython_01.pdf
    ТипДокументы
    #351981
    страница67 из 98
    1   ...   63   64   65   66   67   68   69   70   ...   98
    Функция reload может не оказывать влияния,
    если импорт осуществлялся инструкцией from
    Это еще одна ловушка, связанная с инструкцией from: как уже говори
    лось ранее, инструкция from копирует (присваивает) имена при выпол
    нении, поэтому нет никакой обратной связи с модулем, откуда были скопированы имена. Имена, скопированные инструкцией from, просто становятся ссылками на объекты, на которые ссылались по тем же име
    нам в импортируемом модуле, когда была выполнена инструкция from.
    Вследствие этого повторная загрузка импортируемого модуля может не оказывать воздействия на клиентов, которые импортировали его имена с помощью инструкции from. То есть имена в модуляхклиентах будут попрежнему ссылаться на оригинальные объекты, полученные инструкцией from, даже если имена в оригинальном модуле будут пе
    реопределены:
    from module import X # X может не измениться в результате перезагрузки!

    Типичные проблемы при работе с модулями
    551
    reload(module) # Изменится модуль, но не мои имена
    X # Попрежнему ссылается на старый объект
    Чтобы сделать повторную загрузку более эффективной, используйте инструкцию import и полные имена переменных вместо инструкции from
    . Поскольку полные имена всегда ведут обратно в импортирован
    ный модуль, после повторной загрузки они автоматически будут свя
    заны с новыми именами в модуле.
    import module # Получить объект модуля, а не имена
    reload(module) # Изменит непосредственно сам объект модуля
    module.X # Текущее значение X: отражает результат перезагрузки
    reload, from и тестирование в интерактивной оболочке
    В главе 3 уже говорилось, что изза возникающих сложностей лучше не использовать операции импорта и повторной загрузки для запуска про
    грамм. Дело еще больше осложняется, когда в игру вступает инструк
    ция from. Начинающие осваивать язык Python часто сталкиваются с проблемой, описываемой здесь. Представим, что после открытия мо
    дуля в окне редактирования текста вы запускаете интерактивный сеанс,
    чтобы загрузить и протестировать модуль с помощью инструкции from:
    from module import function function(1, 2, 3)
    Отыскав ошибку, вы возвращаетесь в окно редактирования, исправ
    ляете ее и пытаетесь повторно загрузить модуль следующим способом:
    reload(module)
    Но вы не получите ожидаемого эффекта – инструкция from создала имя function
    , но не создала имя module. Чтобы сослаться на модуль в функ
    ции reload, его сначала необходимо импортировать инструкцией import:
    import module reload(module)
    function(1, 2, 3)
    Однако и в этом случае вы не получите ожидаемого эффекта – функ
    ция reload обновит объект модуля, но, как говорилось в предыдущем разделе, имена, такие как function, скопированные из модуля ранее,
    попрежнему продолжают ссылаться на старые объекты (в данном слу
    чае – на первоначальную версию функции). Чтобы действительно по
    лучить доступ к новой версии функции, после перезагрузки модуля ее необходимо вызывать как module.function или повторно запустить ин
    струкцию from:
    import module reload(module)
    from module import function function(1, 2, 3)

    552
    Глава 21. Дополнительные возможности модулей
    Теперь наконецто нам удалось запустить новую версию функции.
    Как видите, прием, основанный на использовании функции reload в па
    ре с инструкцией from, имеет врожденные проблемы: вы не только должны не забывать перезагружать модуль после импорта, но и не за
    бывать повторно запускать инструкции from после перезагрузки моду
    ля. Это достаточно сложно, чтобы время от времени сбивать с толку да
    же опытного программиста.
    Вы не должны ожидать, что функция reload и инструкция from будут безукоризненно работать в паре. Лучше всего вообще не объединять их –
    используйте функцию reload в паре с инструкцией import или запус
    кайте свои программы другими способами, как предлагалось в главе 3
    (например, выбирая пункт
    Run
    →Run Module (Запустить→Запустить модуль)
    в меню среды разработки IDLE щелчком мыши на ярлыке файла или из командной строки системы).
    Функция reload не обладает транзитивностью
    Когда выполняется повторная загрузка модуля, интерпретатор переза
    гружает только данный конкретный файл модуля – он не выполняет повторную загрузку модулей, которые были импортированы перезагру
    жаемым модулем. Например, если выполняется перезагрузка некото
    рого модуля A, и A импортирует модули B и C, перезагружен будет только модуль A, но не B и C. Инструкции внутри модуля A, которые импортиру
    ют модули B и C, будут перезапущены в процессе перезагрузки, но они просто вернут объекты уже загруженных модулей B и C (предполагает
    ся, что к этому моменту они уже были импортированы). Чтобы было бо
    лее понятно, ниже приводится пример содержимого файла A.py:
    import B # Эти модули не будут перезагружены вместе с A
    import C # Просто будут импортированы уже загруженные модули
    % python
    >>> . . .
    >>> reload(A)
    Не следует полагаться на транзитивную перезагрузку модулей – лучше несколько раз вызывайте функцию reload для непосредственного об
    новления субкомпонентов. При желании можно предусмотреть в про
    грамме автоматическую перезагрузку ее компонентов, добавив вызо
    вы reload в родительские модули, каким здесь является A.
    Еще лучше было бы написать универсальный инструмент для автома
    тического выполнения рекурсивной перезагрузки модулей, сканируя содержимое атрибутов __dict__ модулей (смотрите раздел «Модули –
    это объекты: метапрограммы», выше в этой главе) и проверяя атрибут type в каждом элементе (глава 9), чтобы отыскать вложенные модули.
    Такие вспомогательные функции могут рекурсивно вызывать сами се
    бя и обходить произвольные цепочки импортирования.

    Типичные проблемы при работе с модулями
    553
    Например, модуль reloadall.py, в листинге ниже, содержит функцию reload_all
    , автоматически выполняющую перезагрузку модуля, каж
    дого импортируемого им модуля, и т. д., до самого конца каждой це
    почки импортирования. Она использует словарь, с помощью которого отыскивает уже загруженные модули, рекурсию – для обхода цепочек импорта и модуль types из стандартной библиотеки (был представлен в конце главы 9), в котором просто предопределены значения атрибута type для всех встроенных типов.
    Чтобы воспользоваться этой утилитой, импортируйте функцию relo
    ad_all и передайте ей имя уже загруженного модуля (как если бы это была встроенная функция reload). Когда файл запускается как само
    стоятельная программа, его код самопроверки выполнит проверку са
    мого модуля – он должен импортировать самого себя, потому что его собственное имя не будет определено в файле без инструкции импорти
    рования. Я рекомендую поэкспериментировать с этим примером само
    стоятельно:
    import types def status(module):
    print 'reloading', module.__name__
    def transitive_reload(module, visited):
    if not visited.has_key(module): # Пропустить повторные посещения
    status(module) # Перезагрузить модуль
    reload(module) # И посетить дочерние модули
    visited[module] = None for attrobj in module.__dict__.values(): # Для всех атрибутов
    if type(attrobj) == types.ModuleType: # Рекурсия, если модуль
    transitive_reload(attrobj, visited)
    def reload_all(*args):
    visited = { }
    for arg in args:
    if type(arg) == types.ModuleType:
    transitive_reload(arg, visited)
    if __name__ == '__main__':
    import reloadall # Тест: перезагрузить самого себя
    reload_all(reloadall) # Должна перезагрузить этот модуль
    # и модуль types
    Рекурсивный импорт с инструкцией from
    может не работать
    Напоследок я оставил самую странную (и, к счастью, малоизвестную)
    проблему. В ходе импортирования инструкции в файле выполняются от начала и до конца, поэтому необходимо быть внимательнее, когда ис
    пользуются модули, импортирующие друг друга (эта ситуация называ
    ется рекурсивным импортом). Поскольку не все инструкции в модуле могут быть выполнены к моменту запуска процедуры импортирования

    554
    Глава 21. Дополнительные возможности модулей другого модуля, некоторые из его имен могут оказаться еще не опреде
    ленными.
    Если вы используете инструкцию import, чтобы получить модуль цели
    ком, это может иметь, а может не иметь большого значения – имена мо
    дуля не будут доступны, пока позже не будут использованы полные имена для получения их значений. Но если для получения определен
    ных имен используется инструкция from, имейте в виду, у вас будет дос
    туп только к тем именам, которые уже были определены в этом модуле.
    Например, рассмотрим следующие модули, recur1 и recur2. Модуль recur1
    создает имя X и затем импортирует recur2 до того, как присвоит значение имени Y. В этом месте модуль recur2 может импортировать мо
    дуль recur1 целиком с помощью инструкции import (он уже существует во внутренней таблице модулей интерпретатора), но если используется инструкция from, ей будет доступно только имя X, а имя Y, которому бу
    дет присвоено значение только после инструкции import в recur1, еще не существует, поэтому возникнет ошибка:
    # Файл: recur1.py
    X = 1
    import recur2 # Запустить recur2, если он еще не был импортирован
    Y = 2
    # Файл: recur2.py from recur1 import X # OK: "X" уже имеет значение
    from recur1 import Y # Ошибка: "Y" еще не существует
    >>> import recur1
    Traceback (innermost last):
    File "", line 1, in ?
    File "recur1.py", line 2, in ?
    import recur2
    File "recur2.py", line 2, in ?
    from recur1 import Y # Ошибка: "Y" еще не существует
    ImportError: cannot import name Y
    При рекурсивном импорте модуля recur1 и модуля recur2 интерпрета
    тор не будет повторно выполнять инструкции модуля recur1 (в против
    ном случае это могло бы привести к бесконечному циклу), но про
    странство имен модуля recur1 еще не заполнено до конца к моменту,
    когда он импортируется модулем recur2.
    Решение? Не используйте инструкцию from в операции рекурсивного импорта (в самом деле!). Интерпретатор не зациклится, если вы всета
    ки сделаете это, но ваша программа попадет в зависимость от порядка следования инструкций в модулях.
    Существует два способа решения этой проблемы:

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

    В заключение
    555

    Если от циклов не удается избавиться полностью, попробуйте от
    срочить обращение к именам модуля, используя инструкции import и полные имена (вместо инструкции from), или поместите инструк
    ции from либо внутрь функций (чтобы они не вызывались в про
    граммном коде верхнего уровня), либо ближе к концу файла, чтобы отложить их выполнение.
    В заключение
    В этой главе был рассмотрен ряд дополнительных концепций, связан
    ных с модулями. Мы изучили приемы сокрытия данных, включение но
    вых особенностей языка из модуля __future__, возможности использова
    ния переменной __name__, синтаксис относительного импортирования в пакетах и многое другое. Мы также исследовали проблемы проектиро
    вания модулей и познакомились с типичными ошибками при работе с модулями, что позволит вам избежать их в своем программном коде.
    Со следующей главы мы приступим к изучению объектноориентиро
    ванного инструмента языка Python – класса. Большая часть сведений,
    рассмотренных в последних нескольких главах, применима и здесь –
    классы располагаются в модулях и также являются пространствами имен, но они добавляют еще один элемент в поиск атрибутов, который называется «поиск в цепочке наследования». Поскольку это послед
    няя глава в этой части книги, прежде чем углубиться в объектноори
    ентированное программирование, обязательно выполните упражне
    ния к этой части книги. Но перед этим попробуйте ответить на кон
    трольные вопросы главы, чтобы освежить в памяти темы, рассматри
    вавшиеся здесь.
    Закрепление пройденного
    Контрольные вопросы
    1. Что важно знать о переменных в программном коде верхнего уров
    ня модуля, имена которых начинаются с одного символа подчерки
    вания?
    2. Что означает, когда переменная __name__ модуля имеет значение "
    __main__"
    ?
    3. В чем разница между инструкциями from mypkg import spam и from .
    import spam
    ?
    4. Если пользователь вводит имя модуля в ответ на запрос програм
    мы, как импортировать этот модуль?
    5. Чем отличается изменение списка sys.path от изменения значения переменной окружения PYTHONPATH?
    6. Импорт будущих изменений в языке возможен с помощью модуля
    __future__
    , а возможен ли импорт из прошлого?

    556
    Глава 21. Дополнительные возможности модулей
    Ответы
    1. Переменные в программном коде верхнего уровня модуля, чьи име
    на начинаются с одного символа подчеркивания, не копируются при импортировании с помощью инструкции from *. Однако они доступны при использовании инструкции import и обычной формы инструкции from.
    2. Если переменная __name__ модуля содержит строку "__main__", это означает, что файл выполняется как самостоятельный сценарий,
    а не был импортирован в качестве модуля другим файлом в програм
    ме. То есть файл используется как программа, а не как библиотека.
    3. Инструкция from mypkg import spam выполняет импортирование в аб
    солютном режиме, используя путь поиска в sys.path. Инструкция from . import spam
    , напротив, выполняет импорт в относительном режиме – поиск модуля spam осуществляется сначала в пакете, где находится инструкция, и только потом в sys.path.
    4. Как правило, ввод пользователя поступает в сценарий в виде стро
    ки – чтобы импортировать модуль по имени, заданному в виде стро
    ки, можно собрать и выполнить инструкцию import с помощью ин
    струкции exec или передать строку с именем функции __import__.
    5. Изменения в sys.path воздействуют только на работающую про
    грамму и носят временный характер – изменения будут утеряны сразу же после завершения программы. Содержимое переменной окружения PYTHONPATH хранится в операционной системе – оно ока
    зывает воздействие на все программы, выполняемые на этом ком
    пьютере, а изменения сохраняются после завершения программ.
    6. Нет, мы не можем импортировать из прошлого. Мы можем устано
    вить (или упорно использовать) более старую версию языка, но, как правило, самая лучшая версия – это последняя версия Python.
    1
    Упражнения к пятой части
    Решения приводятся в разделе «Часть V, Модули» приложения A.
    1. Основы импортирования. Напишите программу, которая подсчи
    тывает количество строк и символов в файле (в духе утилиты wc
    в операционной системы UNIX). В своем текстовом редакторе соз
    дайте модуль с именем mymod.py, который экспортирует три имени:

    Функцию countLines(name), которая читает входной файл и под
    считывает число строк в нем (подсказка: большую часть работы можно выполнить с помощью метода file.readlines, а оставшую
    ся часть – с помощью функции len).
    1
    Последняя стабильная версия. – Примеч. науч. ред.

    Закрепление пройденного
    557

    Функцию countChars(name), которая читает входной файл и под
    считывает число символов в нем (подсказка: метод file.read воз
    вращает единую строку).

    Функцию test(name), которая вызывает две предыдущие функ
    ции с заданным именем файла. Вообще говоря, имя файла мож
    но жестко определить в программном коде, принимать ввод име
    ни от пользователя или принимать его как параметр командной строки через список sys.argv,– но пока исходите из предположе
    ния, что оно передается как аргумент функции.
    Все три функции в модуле mymod должны принимать имя файла в ви
    де строки. Если размер любой из функций превысит дветри стро
    ки, это значит, что вы делаете лишнюю работу, – используйте под
    сказки, которые я вам дал!
    Затем проверьте свой модуль в интерактивной оболочке, используя инструкцию import и полные имена экспортируемых функций. Сле
    дует ли добавить в переменную PYTHONPATH каталог, где находится ваш файл mymod.py? Попробуйте проверить модуль на самом себе:
    например, test("mymod.py"). Обратите внимание, что функция test открывает файл дважды – если вы достаточно честолюбивы, попро
    буйте оптимизировать программный код, передавая двум функци
    ям счета объект открытого файла (подсказка: метод file.seek(0) вы
    полняет переустановку указателя в начало файла).
    2. from/from *. Проверьте модуль mymod из упражнения 1 в интерактив
    ной оболочке, используя для загрузки экспортируемых имен инст
    рукцию from – сначала по имени, а потом с помощью формы from *.
    3. __main__. Добавьте в модуль mymod строку, в которой автоматиче
    ски производился бы вызов функции test, только когда модуль вы
    полняется как самостоятельный сценарий, а не во время импорти
    рования. Добавляемая вами строка, вероятно, должна содержать проверку значения атрибута __name__ на равенство строке "__main__",
    как было показано в этой главе. Попробуйте запустить модуль из системной командной строки, затем импортируйте модуль и про
    верьте работу функций в интерактивном режиме. Будут ли рабо
    тать функции в обоих режимах?
    4. Вложенное импортирование. Напишите второй модуль myclient.py,
    который импортирует модуль mymod и проверяет работу его функций,
    затем запустите myclient из системной командной строки. Будут ли доступны функции из mymod на верхнем уровне myclient, если импор
    тировать их с помощью инструкции from? А если они будут импорти
    рованы с помощью инструкции import? Попробуйте реализовать оба варианта в myclient и проверить в интерактивном режиме, импорти
    руя модуль myclient и проверяя содержимое его атрибута __dict__.
    5. Импорт пакетов. Импортируйте ваш файл из пакета. Создайте ка
    талог с именем mypkg, вложенный в каталог, находящийся в пути поиска модулей. Переместите в него файл mymod.py, созданный

    1   ...   63   64   65   66   67   68   69   70   ...   98


    написать администратору сайта