До сих пор все наши программные проекты состояли из одного модуля. Модуль может содержать все, что необходимо для реализации проекта - код, данные и методы. Если модуль один, то он выступает в роли главного модуля со статусом main, так что код модуля запускается на выполнение, в процессе выполнения могут вызываться методы модуля, которые, в свою очередь, вызывают другие методы, так что проект начинает жить, выполняться, пока не достигнет завершения.
На размер модуля никаких ограничений не накладывается. Так что любой сложный проект может быть реализован как единый модуль. Это было бы верно, если бы это не было так сложно. Также как сложный метод должен быть декомпозирован, пока не сведется к совокупности достаточно простых и понятных методов, сложный проект следует декомпозировать на относительно простые модули, каждый со своей спецификой. Если в проекте есть модуль physics и lyrics, то первый содержит сервисы, связанные с физикой, а второй - с лирикой. Конечно, физики и лирики должны общаться, и модули одного проекта должны быть связаны друг с другом. Взаимодействие достигается за счет того, что модуль способен экспортировать свои атрибуты другим модулям, а клиентский модуль может импортировать атрибуты модуля экспортера.
Заметьте, в отличие от традиционного подхода ООП, когда поля и методы класса могут иметь разный уровень доступности - общедоступные, закрытые, защищенные (public, private, protected), все атрибуты модуля общедоступны клиентам.
Пришла пора уточнить понятие атрибута модуля. До сих пор я достаточно вольно обходился с этим понятием, рассматривая, например, взаимодействие атрибутов и методов модуля. В языке Python атрибутами модуля являются все имена, определенные на уровне модуля. Так что если в модуле есть определение def A … и присваивание B = [], то это означает, что модуль имеет два атрибута А и В, один из них представляет функцию, второй - данные. Мне кажется, что иногда крайне важно понимать разницу между данными и методами, поэтому я часто использую понятие атрибут, когда речь идет о данных, и предпочитаю использовать термин метод, когда атрибут является методом. Настоящие программисты питонисты меня будут за это ругать, но для меня Python один из многих языков программирования и потому я не следую строго терминологии, принятой в этом языке. Это же касается понятия метод, который я часто использую там, где по терминологии Python следовало бы использовать термин
функция. Определение def A … - это по определению Python функция, а я предпочитаю термин метод, поскольку для меня функция определяется так, как это принято в математике, - объект, возвращающий значение, а если функция значения не возвращает, выражением не является и вызывается как оператор, то это процедура, но в Python термин процедура не используется, а жаль, поскольку часто важно различать процедуры и функции. Когда же эти различия не важны, то следует использовать общий термин - метод. В контексте этой лекции термин атрибут модуля используется в традиционном для Python смысле, как имя, определенное на уровне модуля, независимо от того, что задает атрибут - данные или метод.
Итак, все атрибуты модуля общедоступны для клиентов. Как же быть, если мы хотим скрыть от клиентов детали реализации? Единственный способ - это соответствующие данные и методы встроить в методы класса, тогда такие атрибуты становятся локальными и не будут доступны клиентам. Инкапсуляция - это полезный прием. Клиентам предоставляются сервисы с тщательно выверенным интерфейсом. Интерфейс сервиса не должен меняться, хотя детали реализации могут изменяться, но это не будет отражаться на клиентах.
Рассмотрим проект, содержащий n модулей. Среди этих модулей должен быть хотя бы один модуль, содержащий код. Среди модулей, содержащих код, должен быть единственный модуль, имеющий статус главного модуля. Как назначить некоторый модуль главным? У модуля помимо атрибутов, определенных текстом модуля, есть встроенные атрибуты, о которых поговорим подробнее чуть позже, а сейчас же скажу, что есть встроенный атрибут __name__. Если присвоить этому атрибуту имя __main__, то модуль и станет главным.
В Visual Studio, где я работаю с проектами Python , все делается проще. В обозревателе решения, нажав на одном из модулей проекта правую кнопку мыши, в контекстном меню следует выбрать пункт меню "Задать как файл запуска". Модуль становится главным и выделяется жирным шрифтом. Модуль становится "точкой большого взрыва", с которой начинается жизнь вселенной, порождаемой программным проектом. В процессе развития проекта модули связываются друг с другом, запрашивая нужные сервисы. Рассмотрим два модуля, связанные отношением "клиент - поставщик". Модуль поставщик предоставляет (экспортирует) атрибуты, а клиент их импортирует.
Пусть прямое имя файла, содержащего модуль поставщика, есть myservices.py, а клиента - Testing_Services.py. Тогда клиенту для импортирования атрибутов поставщика достаточно в коде выполнить предложение импорта:
import myservices
Заметьте, имя модуля в предложении import - это имя файла без уточнения .py и без указания пути к каталогу, содержащему файл с модулем.
Какова семантика инструкции импорта? Достаточно понимать, что в результате выполнения на глобальном уровне модуля Testing_Services будет видимо имя myservices, что позволяет получить доступ ко всем атрибутам модуля myservices, используя квалифицированные имена. Если в модуле myservices есть два выше упомянутых атрибута А и В, то вызов метода А имеет вид: myservices.А(…), а присваивание имеет вид: Х = myservices.В.
Таким образом все атрибуты поставщика доступны клиенту, и мы можем передавать информацию от поставщика клиенту.
Есть еще одна форма импорта атрибутов модуля, синтаксис которой имеет вид:
from <имя модуля> import {<список имен атрибутов> | *}
Семантика этой формы такова. Вначале выполняется импорт модуля, а затем в глобальную область видимости клиента переносятся имена из списка, если он задан, или все имена атрибутов, если использована форма с символом *. Преимущества этой формы в том, что можно не использовать квалифицированные имена, сокращая запись. Но есть и недостатки. Возникают проблемы, связанные с коллизией имен. Импортируемые имена могут совпадать с именами в клиентском модуле. С этой проблемой разобраться нетрудно. Хуже, когда клиент выполняет импорт из разных модулей. Тогда, в особенности, когда применяется форма импорта со звездочкой, разобраться в том, какому модулю принадлежит имя, довольно сложно. В ситуации, когда возможен импорт из разных модулей, надежнее использовать квалифицированные имена.
Удивительно, но при импорте возможна передача информации в обратном направлении - от клиента к поставщику. Ситуация здесь во многом схожа с тем, как метод модуля, работая на локальном уровне, может изменять атрибуты на глобальном уровне, и делает это двумя возможными способами.
При импорте изменение данных поставщика достигается двумя похожими способами. В первом способе изменяется состояние объекта поставщика при сохранении связи имени атрибута и объекта. Во втором способе используется аналог описания global имен. При импорте нет необходимости в задании списка global имен, их роль играют квалифицированные имена.
Рассмотрим пример. Пусть в коде поставщика myservices определены три атрибута:
n = 77; r1 = [3, 5, 7]; r2 = [2, 4, 6, 8]
Пусть модуль Testing_Services, используя форму импорта from, импортировал эти атрибуты. Пусть в исполняемом коде этого модуля встретился такой фрагмент кода:
n = 99; r1[0] = 9; myservices.r2 = [22, 44, 66, 88]
Тогда, в результате первого присваивания, в модуле Testing_Services появится атрибут n, связанный с объектом - числом 99. Это присваивание никак не отразится на атрибуте n модуля myservices. Второе присваивание изменит состояние списка, связанного с атрибутом r1 модуля myservices. Третье присваивание свяжет атрибут r2 модуля myservices с новым списком, поскольку используется квалифицированное имя, позволяющее точно определить, о каком объекте идет речь. Как видите, клиент может изменять атрибуты поставщика!
Уточним семантику выполнения инструкции import <имя модуля>. Заметьте, это выполняемая инструкция, а не декларация. Что происходит при выполнении импорта модуля? Если инструкция импорта модуля встретилась первый раз, то происходит трансляция модуля в промежуточный байт код, создаются все атрибуты модуля, вся эта информация сохраняется.
Возможно повторное выполнение инструкции импорта модуля. При повторном выполнении используется ранее созданная информация. Что нужно понимать? Если модуль импортирован, с ним выполнялись какие-либо действия, значения атрибутов изменялись, затем модуль повторно импортировался, то никакого обновления атрибутов не будет, при работе с модулем будут использоваться существующие до повторного импорта значения атрибутов.
Одно из главных предназначений и достоинств модуля состоит в том, что ранее созданный модуль можно повторно использовать в различных проектах. Возникает проблема, как связать ранее созданный модуль с текущим проектом. Сложность в том, что при импорте модуля указывается только его имя без указания пути, указывающего расположение файла, задающего модуль. Как решается эта проблема в языке Python ?
Интерпретатор Python ищет указанное имя модуля в некоторых фиксированных каталогах. Никаких проблем не возникает, если импортируемый модуль находится в текущем каталоге, где расположен главный модуль. Поиск модуля начинается именно с этого каталога.
Еще одно место, где ищется имя модуля, это каталоги, содержащие библиотеку стандартных модулей. Эта библиотека содержит более 200 модулей и существенно расширяет возможности языка. Она поставляется со всеми версиями языка Python . Так что, если импортируемый модуль - это модуль стандартной библиотеки, то проблем импортирования таких модулей не возникает.
Но каждый программист создает свою собственную библиотеку модулей, предназначенную для повторного использования. Как подключить эту библиотеку к новому проекту. Грубый примитивный подход состоит в том, чтобы скопировать нужные модули из библиотеки и поместить их в текущий каталог проекта. Это не лучший способ.
Намного разумнее использовать функцию path стандартного модуля sys, которая содержит все пути, где Python ищет модуль. Перед тем, как импортировать модуль следует добавить к переменной path путь к каталогу, содержащему нашу личную библиотеку модулей. Такой подход представляет разумную стратегию. Перед тем как импортировать модуль, вы задаете путь к каталогу, содержащему модуль.
Стратегия разумная, единственные недостаток в том, что она не работает при прямом ее использовании. В следующей лекции я подробно исследую проблему импорта модулей и покажу ее решение.
Рассмотрим пример проекта, в котором взаимодействуют два модуля. Один модуль myservices играет роль библиотечного модуля, предоставляющего ряд сервисов. Я включил в него четыре ранее рассмотренных метода:
QSort - быстрая сортировка списков,TranslateFromPToQ - перевод чисел с целой и дробной частью изсистемы счисления P в систему счисления Q,Integral - вычисление определенного интеграла с подынтегральной функцией f(x, p),PolygonSquare - вычисление площади выпуклого многоугольника, заданного списком вершин.Поскольку код всех этих методов приводился в предыдущих лекциях, то повторять его не буду. Замечу только, что все методы, построенные в результате декомпозиции основного метода, являются локальными, так что наш модуль на глобальном уровне содержит определения четырех методов и не содержит никакой другой информации. Это довольно типичная структура библиотечных модулей. Поскольку выполняемого кода в этом модуле нет, то он не может играть роль главного модуля проекта
Другой модуль нашего проекта - TestingServices играет роль интерфейсного модуля. Он обеспечивает связь с пользователем, импортирует сервисы библиотечного модуля, подготавливает входные данные, вызывает сервисы, получает результаты работы сервисов и предоставляет их пользователю.
Такая структура проекта, содержащая как минимум два модуля, характерна даже для небольших проектов, поскольку она поддерживает один из основных принципов профессионального программирования, требующего отделение интерфейса проекта от бизнес-логики проекта.
Интерфейсный модуль всегда содержит выполняемый код и может выступать в роли главного модуля проекта, что и реализовано в нашем проекте. Интерфейсный модуль может содержать и методы, используемые при реализации интерфейса.
Приведу код нашего интерфейсного модуля:
print('Testing services of module "mysevices"')
import myservices
def getlist()->list:
"""
Ввод пользователем списка целых чисел с консоли
"""
n = int(input('Введите число элементов списка. затем сами элементы \n'))
arr = []
for i in range(0, n):
item = int(input())
arr.append(item)
return arr
print('sorting')
arr = getlist()
myservices.QSort(arr)
print (arr)
print('translate from P to Q')
number4 = '12.12'; p = 4; q = 2
number2 = myservices.TranslateFromPtoQ(number4, p, q)
print(number2)
def f(x: float, p: list)-> float:
return p[0] * x * x + p[1] * x + p[2]
print('integral')
integral = myservices.Integral(0, 2, f, [2, 4, 6], 1e-7)
print(integral)
print ('PoligonSquare')
vertices = [(0, 0), (2, 3), (4, 3),(6, 0), (4, -3), (2, -3)]
square = myservices.PolygonSquare(vertices)
print(square)
Как видите, модуль содержит исполняемый код и два метода. Первый из методов позволяет пользователю ввести список с консоли, второй задает подынтегральную функцию при вычислении интеграла.
Вот результаты работы при запуске проекта на выполнение:
На приведенных тестах все сервисы корректно работают.
Мы рассмотрели атрибуты, определяемые модулем. Но все объекты Python - классы, модули, методы, числа имеют встроенные атрибуты, число и специфика которых зависит от типа объекта. Функция dir(<object>) возвращает список всех атрибутов объекта - встроенных и определенных самим объектом.
Давайте посмотрим, каков полный список атрибутов у построенного нами модуля myservices. Я добавил в код главного модуля TestingServices пару строк текста:
print('dir info myservices')
print(dir(myservices))
Вот какие результаты породили эти строки:
Помимо четырех атрибутов, определенных в модуле, в список входят еще восемь встроенных атрибутов.
С одним из этих атрибутов - name - мы уже знакомы. Этот атрибут задает внутреннее имя модуля. Для главного модуля - это имя ___main__. Остальные модули по умолчанию получают имя совпадающее с внешним именем модуля (имя файла). Для модуля myservices встроенный атрибут __name__ имеет значение myservices. Встроенный атрибут __file__, как нетрудно догадаться, задает полный путь к файлу, содержащему модуль. Атрибут __doc__ позволяет задать документацию, характеризующую модуль, по умолчанию он имеет значение None.
Об остальных атрибутах я говорить не буду. У объектов разных типов атрибуты отличаются. При необходимости функция help(<атрибут>) получить справку об атрибуте.
Для интереса давайте получим полный список атрибутов главного модуля TestingServices:
Как видите, встроенные атрибуты у обоих модулей совпадают, а атрибуты, определяемые в модулях, естественно, различаются.
Заметьте, имена встроенных атрибутов декорированы двумя пробелами слева и справа. Декорированные имена переопределять не следует, так что в модуле не должны определяться атрибуты, имена которых совпадают с именами встроенных атрибутов.
До сих пор все наши программные проекты состояли из одного модуля. Модуль может содержать все, что необходимо для реализации проекта - код, данные и методы. Если модуль один, то он выступает в роли главного модуля со статусом main, так что код модуля запускается на выполнение, в процессе выполнения могут вызываться методы модуля, которые, в свою очередь, вызывают другие методы, так что проект начинает жить, выполняться, пока не достигнет завершения.
На размер модуля никаких ограничений не накладывается. Так что любой сложный проект может быть реализован как единый модуль. Это было бы верно, если бы это не было так сложно. Также как сложный метод должен быть декомпозирован, пока не сведется к совокупности достаточно простых и понятных методов, сложный проект следует декомпозировать на относительно простые модули, каждый со своей спецификой. Если в проекте есть модуль physics и lyrics, то первый содержит сервисы, связанные с физикой, а второй - с лирикой. Конечно, физики и лирики должны общаться, и модули одного проекта должны быть связаны друг с другом. Взаимодействие достигается за счет того, что модуль способен экспортировать свои атрибуты другим модулям, а клиентский модуль может импортировать атрибуты модуля экспортера.
Заметьте, в отличие от традиционного подхода ООП, когда поля и методы класса могут иметь разный уровень доступности - общедоступные, закрытые, защищенные (public, private, protected), все атрибуты модуля общедоступны клиентам.
Пришла пора уточнить понятие атрибута модуля. До сих пор я достаточно вольно обходился с этим понятием, рассматривая, например, взаимодействие атрибутов и методов модуля. В языке Python атрибутами модуля являются все имена, определенные на уровне модуля. Так что если в модуле есть определение def A … и присваивание B = [], то это означает, что модуль имеет два атрибута А и В, один из них представляет функцию, второй - данные. Мне кажется, что иногда крайне важно понимать разницу между данными и методами, поэтому я часто использую понятие атрибут, когда речь идет о данных, и предпочитаю использовать термин метод, когда атрибут является методом. Настоящие программисты питонисты меня будут за это ругать, но для меня Python один из многих языков программирования и потому я не следую строго терминологии, принятой в этом языке. Это же касается понятия метод, который я часто использую там, где по терминологии Python следовало бы использовать термин
функция. Определение def A … - это по определению Python функция, а я предпочитаю термин метод, поскольку для меня функция определяется так, как это принято в математике, - объект, возвращающий значение, а если функция значения не возвращает, выражением не является и вызывается как оператор, то это процедура, но в Python термин процедура не используется, а жаль, поскольку часто важно различать процедуры и функции. Когда же эти различия не важны, то следует использовать общий термин - метод. В контексте этой лекции термин атрибут модуля используется в традиционном для Python смысле, как имя, определенное на уровне модуля, независимо от того, что задает атрибут - данные или метод.
Итак, все атрибуты модуля общедоступны для клиентов. Как же быть, если мы хотим скрыть от клиентов детали реализации? Единственный способ - это соответствующие данные и методы встроить в методы класса, тогда такие атрибуты становятся локальными и не будут доступны клиентам. Инкапсуляция - это полезный прием. Клиентам предоставляются сервисы с тщательно выверенным интерфейсом. Интерфейс сервиса не должен меняться, хотя детали реализации могут изменяться, но это не будет отражаться на клиентах.
Рассмотрим проект, содержащий n модулей. Среди этих модулей должен быть хотя бы один модуль, содержащий код. Среди модулей, содержащих код, должен быть единственный модуль, имеющий статус главного модуля. Как назначить некоторый модуль главным? У модуля помимо атрибутов, определенных текстом модуля, есть встроенные атрибуты, о которых поговорим подробнее чуть позже, а сейчас же скажу, что есть встроенный атрибут __name__. Если присвоить этому атрибуту имя __main__, то модуль и станет главным.
В Visual Studio, где я работаю с проектами Python , все делается проще. В обозревателе решения, нажав на одном из модулей проекта правую кнопку мыши, в контекстном меню следует выбрать пункт меню "Задать как файл запуска". Модуль становится главным и выделяется жирным шрифтом. Модуль становится "точкой большого взрыва", с которой начинается жизнь вселенной, порождаемой программным проектом. В процессе развития проекта модули связываются друг с другом, запрашивая нужные сервисы. Рассмотрим два модуля, связанные отношением "клиент - поставщик". Модуль поставщик предоставляет (экспортирует) атрибуты, а клиент их импортирует.
Пусть прямое имя файла, содержащего модуль поставщика, есть myservices.py, а клиента - Testing_Services.py. Тогда клиенту для импортирования атрибутов поставщика достаточно в коде выполнить предложение импорта:
import myservices
Заметьте, имя модуля в предложении import - это имя файла без уточнения .py и без указания пути к каталогу, содержащему файл с модулем.
Какова семантика инструкции импорта? Достаточно понимать, что в результате выполнения на глобальном уровне модуля Testing_Services будет видимо имя myservices, что позволяет получить доступ ко всем атрибутам модуля myservices, используя квалифицированные имена. Если в модуле myservices есть два выше упомянутых атрибута А и В, то вызов метода А имеет вид: myservices.А(…), а присваивание имеет вид: Х = myservices.В.
Таким образом все атрибуты поставщика доступны клиенту, и мы можем передавать информацию от поставщика клиенту.
Есть еще одна форма импорта атрибутов модуля, синтаксис которой имеет вид:
from <имя модуля> import {<список имен атрибутов> | *}
Семантика этой формы такова. Вначале выполняется импорт модуля, а затем в глобальную область видимости клиента переносятся имена из списка, если он задан, или все имена атрибутов, если использована форма с символом *. Преимущества этой формы в том, что можно не использовать квалифицированные имена, сокращая запись. Но есть и недостатки. Возникают проблемы, связанные с коллизией имен. Импортируемые имена могут совпадать с именами в клиентском модуле. С этой проблемой разобраться нетрудно. Хуже, когда клиент выполняет импорт из разных модулей. Тогда, в особенности, когда применяется форма импорта со звездочкой, разобраться в том, какому модулю принадлежит имя, довольно сложно. В ситуации, когда возможен импорт из разных модулей, надежнее использовать квалифицированные имена.
Удивительно, но при импорте возможна передача информации в обратном направлении - от клиента к поставщику. Ситуация здесь во многом схожа с тем, как метод модуля, работая на локальном уровне, может изменять атрибуты на глобальном уровне, и делает это двумя возможными способами.
При импорте изменение данных поставщика достигается двумя похожими способами. В первом способе изменяется состояние объекта поставщика при сохранении связи имени атрибута и объекта. Во втором способе используется аналог описания global имен. При импорте нет необходимости в задании списка global имен, их роль играют квалифицированные имена.
Рассмотрим пример. Пусть в коде поставщика myservices определены три атрибута:
n = 77; r1 = [3, 5, 7]; r2 = [2, 4, 6, 8]
Пусть модуль Testing_Services, используя форму импорта from, импортировал эти атрибуты. Пусть в исполняемом коде этого модуля встретился такой фрагмент кода:
n = 99; r1[0] = 9; myservices.r2 = [22, 44, 66, 88]
Тогда, в результате первого присваивания, в модуле Testing_Services появится атрибут n, связанный с объектом - числом 99. Это присваивание никак не отразится на атрибуте n модуля myservices. Второе присваивание изменит состояние списка, связанного с атрибутом r1 модуля myservices. Третье присваивание свяжет атрибут r2 модуля myservices с новым списком, поскольку используется квалифицированное имя, позволяющее точно определить, о каком объекте идет речь. Как видите, клиент может изменять атрибуты поставщика!
Уточним семантику выполнения инструкции import <имя модуля>. Заметьте, это выполняемая инструкция, а не декларация. Что происходит при выполнении импорта модуля? Если инструкция импорта модуля встретилась первый раз, то происходит трансляция модуля в промежуточный байт код, создаются все атрибуты модуля, вся эта информация сохраняется.
Возможно повторное выполнение инструкции импорта модуля. При повторном выполнении используется ранее созданная информация. Что нужно понимать? Если модуль импортирован, с ним выполнялись какие-либо действия, значения атрибутов изменялись, затем модуль повторно импортировался, то никакого обновления атрибутов не будет, при работе с модулем будут использоваться существующие до повторного импорта значения атрибутов.
Одно из главных предназначений и достоинств модуля состоит в том, что ранее созданный модуль можно повторно использовать в различных проектах. Возникает проблема, как связать ранее созданный модуль с текущим проектом. Сложность в том, что при импорте модуля указывается только его имя без указания пути, указывающего расположение файла, задающего модуль. Как решается эта проблема в языке Python ?
Интерпретатор Python ищет указанное имя модуля в некоторых фиксированных каталогах. Никаких проблем не возникает, если импортируемый модуль находится в текущем каталоге, где расположен главный модуль. Поиск модуля начинается именно с этого каталога.
Еще одно место, где ищется имя модуля, это каталоги, содержащие библиотеку стандартных модулей. Эта библиотека содержит более 200 модулей и существенно расширяет возможности языка. Она поставляется со всеми версиями языка Python . Так что, если импортируемый модуль - это модуль стандартной библиотеки, то проблем импортирования таких модулей не возникает.
Но каждый программист создает свою собственную библиотеку модулей, предназначенную для повторного использования. Как подключить эту библиотеку к новому проекту. Грубый примитивный подход состоит в том, чтобы скопировать нужные модули из библиотеки и поместить их в текущий каталог проекта. Это не лучший способ.
Намного разумнее использовать функцию path стандартного модуля sys, которая содержит все пути, где Python ищет модуль. Перед тем, как импортировать модуль следует добавить к переменной path путь к каталогу, содержащему нашу личную библиотеку модулей. Такой подход представляет разумную стратегию. Перед тем как импортировать модуль, вы задаете путь к каталогу, содержащему модуль.
Стратегия разумная, единственные недостаток в том, что она не работает при прямом ее использовании. В следующей лекции я подробно исследую проблему импорта модулей и покажу ее решение.
Рассмотрим пример проекта, в котором взаимодействуют два модуля. Один модуль myservices играет роль библиотечного модуля, предоставляющего ряд сервисов. Я включил в него четыре ранее рассмотренных метода:
QSort - быстрая сортировка списков,TranslateFromPToQ - перевод чисел с целой и дробной частью изсистемы счисления P в систему счисления Q,Integral - вычисление определенного интеграла с подынтегральной функцией f(x, p),PolygonSquare - вычисление площади выпуклого многоугольника, заданного списком вершин.Поскольку код всех этих методов приводился в предыдущих лекциях, то повторять его не буду. Замечу только, что все методы, построенные в результате декомпозиции основного метода, являются локальными, так что наш модуль на глобальном уровне содержит определения четырех методов и не содержит никакой другой информации. Это довольно типичная структура библиотечных модулей. Поскольку выполняемого кода в этом модуле нет, то он не может играть роль главного модуля проекта
Другой модуль нашего проекта - TestingServices играет роль интерфейсного модуля. Он обеспечивает связь с пользователем, импортирует сервисы библиотечного модуля, подготавливает входные данные, вызывает сервисы, получает результаты работы сервисов и предоставляет их пользователю.
Такая структура проекта, содержащая как минимум два модуля, характерна даже для небольших проектов, поскольку она поддерживает один из основных принципов профессионального программирования, требующего отделение интерфейса проекта от бизнес-логики проекта.
Интерфейсный модуль всегда содержит выполняемый код и может выступать в роли главного модуля проекта, что и реализовано в нашем проекте. Интерфейсный модуль может содержать и методы, используемые при реализации интерфейса.
Приведу код нашего интерфейсного модуля:
print('Testing services of module "mysevices"')
import myservices
def getlist()->list:
"""
Ввод пользователем списка целых чисел с консоли
"""
n = int(input('Введите число элементов списка. затем сами элементы \n'))
arr = []
for i in range(0, n):
item = int(input())
arr.append(item)
return arr
print('sorting')
arr = getlist()
myservices.QSort(arr)
print (arr)
print('translate from P to Q')
number4 = '12.12'; p = 4; q = 2
number2 = myservices.TranslateFromPtoQ(number4, p, q)
print(number2)
def f(x: float, p: list)-> float:
return p[0] * x * x + p[1] * x + p[2]
print('integral')
integral = myservices.Integral(0, 2, f, [2, 4, 6], 1e-7)
print(integral)
print ('PoligonSquare')
vertices = [(0, 0), (2, 3), (4, 3),(6, 0), (4, -3), (2, -3)]
square = myservices.PolygonSquare(vertices)
print(square)
Как видите, модуль содержит исполняемый код и два метода. Первый из методов позволяет пользователю ввести список с консоли, второй задает подынтегральную функцию при вычислении интеграла.
Вот результаты работы при запуске проекта на выполнение:
На приведенных тестах все сервисы корректно работают.
Мы рассмотрели атрибуты, определяемые модулем. Но все объекты Python - классы, модули, методы, числа имеют встроенные атрибуты, число и специфика которых зависит от типа объекта. Функция dir(<object>) возвращает список всех атрибутов объекта - встроенных и определенных самим объектом.
Давайте посмотрим, каков полный список атрибутов у построенного нами модуля myservices. Я добавил в код главного модуля TestingServices пару строк текста:
print('dir info myservices')
print(dir(myservices))
Вот какие результаты породили эти строки:
Помимо четырех атрибутов, определенных в модуле, в список входят еще восемь встроенных атрибутов.
С одним из этих атрибутов - name - мы уже знакомы. Этот атрибут задает внутреннее имя модуля. Для главного модуля - это имя ___main__. Остальные модули по умолчанию получают имя совпадающее с внешним именем модуля (имя файла). Для модуля myservices встроенный атрибут __name__ имеет значение myservices. Встроенный атрибут __file__, как нетрудно догадаться, задает полный путь к файлу, содержащему модуль. Атрибут __doc__ позволяет задать документацию, характеризующую модуль, по умолчанию он имеет значение None.
Об остальных атрибутах я говорить не буду. У объектов разных типов атрибуты отличаются. При необходимости функция help(<атрибут>) получить справку об атрибуте.
Для интереса давайте получим полный список атрибутов главного модуля TestingServices:
Как видите, встроенные атрибуты у обоих модулей совпадают, а атрибуты, определяемые в модулях, естественно, различаются.
Заметьте, имена встроенных атрибутов декорированы двумя пробелами слева и справа. Декорированные имена переопределять не следует, так что в модуле не должны определяться атрибуты, имена которых совпадают с именами встроенных атрибутов.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.