НОЧУ ДПО "Национальный открытый университет "ИНТУИТ"
Опубликован: 24.01.2021 | Доступ: свободный | Студентов: 2489 / 106 | Длительность: 03:57:00
Лекция 17:

Модульное программирование

< Лекция 1 || Лекция 17: 12

Смотреть на youtube

Проект для лекции Lecture6.rar.

Язык Python поддерживает разные стили программирования. Все они предназначены для борьбы со сложностью программных проектов. Функциональное программирование работает на уровне метода, позволяя в результате декомпозиции представить метод в виде композиции более простых методов. Модульное программирование работает на более высоком уровне, когда проект разрабатывается, не как единый модуль, а представляется в результате декомпозиции в виде композиции более простых модулей. Например, если проект предназначен для моделирования работы некоторого предприятия, то в проекте могут появиться такие модули, как "Финансы", "Производство", "Склад", "Логистика". При необходимости каждый из этих модулей может быть декомпозирован на более простые модули.

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

Функциональное и модульное программирование неразрывно связаны друг с другом. Вы не можете создать функцию Python , не создав предварительно модуль. Модуль является контейнером, в который помещаются функции. С другой стороны, модуль, во многом это набор функций, или, пользуясь более общим термином, набор методов, каждый из которых представляет сервис, поставляемый модулем своим клиентам. Во многих случаях модуль ничего не содержит кроме методов. Когда мы разрабатываем отдельный модуль, то мы занимаемся функциональным программированием - программированием в функциях, как это было описано в предыдущей лекции. Но когда мы переходим на более высокий уровень проектирования большого программного проекта, то нам приходится заниматься программированием в модулях, выполняя декомпозицию большой системы на совокупность взаимодействующих модулей.

Любая программа на Python содержит хотя бы один модуль. Давайте вспомним примеры программ, приводимых в прошлых лекциях, и посмотрим, как устроены модули в этих примерах. В лекции, посвященной стилям программирования, рассматривались простейшие программы, представляющие выполняемую последовательность операторов языка Python . Как создать проект такого типа? Достаточно в любом текстовом редакторе открыть файл, записать в него программу на Python , состоящую из последовательности операторов, сохранить файл с уточнением .py. В результате будет создан модуль, который может быть выполнен интерпретатором Python .

Справедливы следующие утверждения:

  • Любой модуль Python создается и хранится как текстовый файл с уточнением .py.
  • Модуль может содержать программу на языке Python .
  • Если проект содержит несколько модулей, то одному из этих модулей можно назначить статус запускаемого модуля. Программа этого модуля получает по умолчанию имя __main__ (главная программа) и выполнение проекта начинается с выполнения этой программы.

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

Справедливо следующее утверждение:

  • Модуль может содержать определения методов модуля.

Заметьте, определение - это определение, а не выполняемый код. Чтобы выполнить метод, он должен быть вызван.

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

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

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

В наших примерах создавался бизнес-метод X и соответствующий ему интерфейсный метод - процедура с именем testX. Программа main модуля вызывала метод testX, который в свою очередь вызывал соответствующий сервис - бизнес метод X.

Модуль может быть устроен более сложно, - иметь не только методы, но и атрибуты. Сегодняшняя лекция будет посвящена более полному описанию структуры модуля. Но, вначале, продолжая тему функционального программирования, хочу остановиться на некоторых аспектах построения методов.

Функции. Способы передачи информации.

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

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

В лекции по функциональному программированию мы определили функцию высшего порядка Integral, вычисляющую значение определенного интеграла, которой в качестве формального параметра передавалась подынтегральная функция. Тогда мы упростили задачу, полагая, что подынтегральная функция зависит только от одного аргумента. На практике подынтегральная функция может зависеть от многих параметров. Например, используемая нами функция f(x) = 2x + 1 является частным случаем линейной функции ax + b, для вычисления значений которой в общем случае требуется знать не только аргумент x, но и значения параметров a и b. Рассматриваемая там же функция g(x) является частным случаем квадратичной функции, у которой помимо x есть еще три параметра. Функция F(x) = f(g(x)), представляющая суперпозицию функций, также имеет три параметра - аргумент x и два параметра, задающие функции, участвующие в суперпозиции. Как передать эту информацию функции высшего порядка Integral, учитывая, что разным подынтегральным функциям требуется разнородная информация. Рассмотрим способы решения этой задачи.

Начнем с классического варианта, когда для передачи информации используются входные параметры. Будем полагать, что подынтегральная функция - это функция от двух параметров. Первый параметр - это параметр x, - точка, в которой вычисляется функция. А второй параметр params - это список, содержащий всю необходимую дополнительную информацию. Это изменение в малой степени коснется функции Integral, - параметр params появится в списке формальных параметров и будет передаваться подынтегральной функции при ее вызове.

def Integral_M(a: float, b: float, f, params: list, eps: float)-> float:
    """
    Вычисление определенного интеграла на интервале [a, b]
    с подынтегральной функцией f(x, params).
    Интеграл вычисляется как площадь.
    eps - точность вычислений
    """

    n = 1;    d = b - a;    s0 = -1;    h = d/2
    sf = (math.fabs(f(a, params)) + math.fabs(f(b, params)))/2
    s1 = sf * d
    while math.fabs(s1 - s0) > eps:  
        for i in range(n):
            sf += math.fabs(f(a + h + 2 * i * h, params))        
        s0 = s1;  s1 = sf * h; n = 2 * n;  h = h /2
    return s1

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

import math
def f(x: float)-> float:
    return 2 * x + 1
def g(y: float)-> float:
    return y * y + 4 * y - 5
def F (x: (int, float), f, g)-> float:
    """
    Функция как объект. F- Функция высших порядков - аргументы f, g являются функциями  
    """
    return f(g(x)) 
def test1():
    """
    Вызовы обновленной функции Integral_M
    Подынтегральные функции задаются с использованием лямбда определений
    """
    eps = 1e-7
    pars = [2, 1]
    mix = Integral_M(0, 2, lambda x, pars: pars[0] * x + pars[1], pars, eps )
    print(mix)
    pars = [1, 4, -5]
    mix = Integral_M(0, 2, lambda x, pars: pars[0] *x *x + pars[1] * x + pars[2], pars, eps)
    print(mix)
    pars = [f, g]
    mix = Integral_M(0, 2, lambda x, pars: pars[0](pars[1](x)), pars, eps)
    print(mix)
test1()

Вот результаты работы:


< Лекция 1 || Лекция 17: 12
Алексей Авилов
Алексей Авилов

Неужели не нашлось русских специалистов, чтобы записать курс по пайтону ? Да, можно включить переводчик и слушать с переводом, но это что? Это кто-то считает хорошим и понятным курсом для начинающих? 

Елена Лаптева
Елена Лаптева

Думаю. что не смогу его закончить. Хотелось предупредить других - не тратьте зря время, ищите другой курс.