Модульное программирование с функциями

Beginner

This tutorial is from open-source community. Access the source code

Введение

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

Это Guided Lab, который предоставляет пошаговые инструкции, чтобы помочь вам учиться и практиковаться. Внимательно следуйте инструкциям, чтобы выполнить каждый шаг и получить практический опыт. Исторические данные показывают, что это лабораторная работа уровня начальный с процентом завершения 82%. Он получил 100% положительных отзывов от учащихся.

Модули и импорт

Любой исходный файл на Python является модулем.

## foo.py
def grok(a):
 ...
def spam(b):
 ...

Инструкция import загружает и исполняет модуль.

## program.py
import foo

a = foo.grok(2)
b = foo.spam('Hello')
...

Пространства имен

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

import foo

a = foo.grok(2)
b = foo.spam('Hello')
...

Имя модуля напрямую связано с именем файла (foo -> foo.py).

Глобальные определения

Все, определенное в глобальном пространстве имен, заполняет пространство имен модуля. Рассмотрим два модуля, которые определяют одну и ту же переменную x.

## foo.py
x = 42
def grok(a):
 ...
## bar.py
x = 37
def spam(a):
 ...

В этом случае определения x ссылаются на разные переменные. Одна из них - foo.x, а другая - bar.x. Разные модули могут использовать одинаковые имена, и эти имена не будут конфликтовать между собой.

Модули изолированы.

Модули в качестве окружений

Модули образуют окружающее окружение для всего кода, определенного внутри них.

## foo.py
x = 42

def grok(a):
    print(x)

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

Выполнение модуля

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

Инструкция import as

Вы можете изменить имя модуля при импорте:

import math as m
def rectangular(r, theta):
    x = r * m.cos(theta)
    y = r * m.sin(theta)
    return x, y

Это работает так же, как обычный импорт. Просто переименовывает модуль в этом файле.

Импорт из модуля с использованием from

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

from math import sin, cos

def rectangular(r, theta):
    x = r * cos(theta)
    y = r * sin(theta)
    return x, y

Это позволяет использовать части модуля без необходимости указывать префикс модуля. Это полезно для часто используемых имен.

Комментарии по импорту

Различные способы импорта не изменяют то, как работают модули.

import math
## vs
import math as m
## vs
from math import cos, sin
...

В частности, import всегда выполняет целый файл, и модули по-прежнему являются изолированными окружениями.

Инструкция import module as только изменяет имя локально. Инструкция from math import cos, sin по-прежнему загружает весь модуль math "под капотом". Просто копирует имена cos и sin из модуля в локальное пространство после того, как это сделано.

Загрузка модуля

Каждый модуль загружается и выполняется только один раз. Примечание: Повторный импорт просто возвращает ссылку на ранее загруженный модуль.

sys.modules - это словарь всех загруженных модулей.

>>> import sys
>>> sys.modules.keys()
['copy_reg', '__main__', 'site', '__builtin__', 'encodings', 'encodings.encodings', 'posixpath',...]
>>>

Внимание: Часто возникает путаница, если вы повторяете инструкцию import после изменения исходного кода модуля. В силу кеша модулей sys.modules, повторный импорт всегда возвращает ранее загруженный модуль - даже если были внесены изменения. Самый безопасный способ загрузить измененный код в Python - выйти и перезапустить интерпретатор.

Поиск модулей

Python обращается к списку путей (sys.path), когда ищет модули.

>>> import sys
>>> sys.path
[
  '',
  '/usr/local/lib/python36/python36.zip',
  '/usr/local/lib/python36',
...
]

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

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

Как уже упоминалось, sys.path содержит пути поиска. Вы можете вручную настроить его, если это необходимо.

import sys
sys.path.append('/project/foo/pyfiles')

Пути можно также добавлять с помощью переменных окружения.

% env PYTHONPATH=/project/foo/pyfiles python3
Python 3.6.0 (default, Feb 3 2017, 05:53:21)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)]
>>> import sys
>>> sys.path
['','/project/foo/pyfiles',...]

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

Для этого упражнения, связанного с модулями, крайне важно убедиться, что вы запускаете Python в правильной среде. Модули часто создают проблемы для начинающих программистов, связанные с текущей рабочим директорией или настройками пути Python. Для этого курса предполагается, что вы пишете весь свой код в директории ~/project. Для наилучших результатов вы должны убедиться, что также находитесь в этой директории, когда запускаете интерпретатор. Если нет, вам нужно убедиться, что ~/project добавлено в sys.path.

Упражнение 3.11: Импорт модулей

В разделе 3 мы создали универсальную функцию parse_csv() для разбора содержимого CSV-файлов с данными.

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

Запустите интерактивный режим Python.

$ python3
Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

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

>>> import bounce
... посмотрите на вывод...
>>> import mortgage
... посмотрите на вывод...
>>> import report
... посмотрите на вывод...
>>>

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

>>> import fileparse
>>> help(fileparse)
... посмотрите на вывод...
>>> dir(fileparse)
... посмотрите на вывод...
>>>

Попробуйте использовать модуль для чтения некоторых данных:

>>> portfolio = fileparse.parse_csv('/home/labex/project/portfolio.csv',select=['name','shares','price'], types=[str,int,float])
>>> portfolio
... посмотрите на вывод...
>>> pricelist = fileparse.parse_csv('/home/labex/project/prices.csv',types=[str,float], has_headers=False)
>>> pricelist
... посмотрите на вывод...
>>> prices = dict(pricelist)
>>> prices
... посмотрите на вывод...
>>> prices['IBM']
106.28
>>>

Попробуйте импортировать функцию, чтобы не нужно было указывать имя модуля:

>>> from fileparse import parse_csv
>>> portfolio = parse_csv('/home/labex/project/portfolio.csv', select=['name','shares','price'], types=[str,int,float])
>>> portfolio
... посмотрите на вывод...
>>>

Упражнение 3.12: Использование вашего библиотеческого модуля

В разделе 2 вы написали программу report.py, которая генерировала отчет по активам, похожий на этот:

      Name     Shares      Price     Change
---------- ---------- ---------- ----------
        AA        100       9.22     -22.98
       IBM         50     106.28      15.18
       CAT        150      35.46     -47.98
      MSFT        200      20.89     -30.34
        GE         95      13.48     -26.89
      MSFT         50      20.89     -44.21
       IBM        100     106.28      35.84

Взять эту программу и изменить ее так, чтобы все обработка входных файлов выполнялась с использованием функций из вашего модуля fileparse. Для этого импортировать fileparse в качестве модуля и изменить функции read_portfolio() и read_prices(), чтобы они использовали функцию parse_csv().

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

Упражнение 3.13: Сделано специально пустым (пропустить)

Упражнение 3.14: Использование дополнительных импортов библиотеки

В разделе 1 вы написали программу pcost.py, которая читала портфель и вычисляла его стоимость.

>>> import pcost
>>> pcost.portfolio_cost('/home/labex/project/portfolio.csv')
44671.15
>>>

Измените файл pcost.py так, чтобы он использовал функцию report.read_portfolio().

Комментарий

После завершения этого упражнения у вас должны быть три программы. fileparse.py, в котором содержится универсальная функция parse_csv(). report.py, которая генерирует красивый отчет, но также содержит функции read_portfolio() и read_prices(). И, наконец, pcost.py, которая вычисляет стоимость портфеля, но использует функцию read_portfolio(), написанную для программы report.py.

Резюме

Поздравляем! Вы завершили лабораторную работу по модулям. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.