Модули и их импорт. Урок 8

Методическая разработка урока
Элективный курс: Введение в объектно-ориентированное программирование на Python
Уровень: Программирование для начинающих

На прошлом уроке нами была написана "серьезная" программа, которую могут использовать другие программисты. Однако как? Просто копировать код и вставлять в свои скрипты? Или есть более экономный способ (в смысле уменьшения объема кода и удобства его использования)?

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

Программные модули

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

Так как же импортировать содержимое одного файла в другой в языке программирования Python? Существует два основных способа: инструкция import и инструкция from. Первая инструкция запускает (интерпретирует) файл-модуль полностью, при этом для доступа к переменным (атрибутам) модуля из основного файла следует указывать имя модуля впереди требуемого атрибута: module.attribute (так называемая, точечная нотация). Инструкция from передает интерпретатору лишь указанные имена из файла-модуля, однако при доступе к этим переменным не надо указывать имя модуля. Первый способ хорош, если предстоит пользоваться содержимым почти всего модуля, второй — если будут востребованы одна-две функции или класс из модуля. В примере данного урока мы воспользуемся инструкцией import.

Импорт в языке программирования Python осуществляется следующим образом: после слова import пишется имя импортируемого модуля. Модуль и файл в Python понятия почти не различимые. Файлы с кодом на языке Python обычно имеют расширение .py, однако в инструкции import расширение не указывается. Например, если мы имеем файл-модуль scale.py, то импортировать его в другой файл следует так: import scale.

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

Итак, у нас есть файл с кодом, позволяющим вычислять оклеиваемую площадь помещения (урок №7). Пусть он называется rooms.py. Кроме того, удалим из него "код тестирования" …

labor34 = Room(5,4,2)
labor34.win_door(1.5,1.5, 2,1, 2)
labor34.wallpapers()
labor34.printer()
 
print(labor34.window.square)
print(labor34.door.square)

… и предположим, что классы этого модуля будут использоваться в другом (основном) файле. Допустим, этот основной файл предоставляет интерфейс пользователю для ввода данных и получения результата. Основной файл должен быть сохранен в том же каталоге, что и файл rooms.py.

Первым делом, импортируем содержимое файла rooms.py

import rooms

Далее организуем запрос данных у пользователя, одновременно преобразовав данные в целочисленный тип (функция int):

print ("Введите размеры помещения (в метрах) ...")
l = int(input ("длина: "))
w = int(input ("ширина: "))
h = int(input ("высота: "))
 
print ("Введите данные об оконных проемах (в метрах) ...")
h_w = int(input ("высота: "))
w_w = int(input ("ширина: "))
m = int(input ("количество: "))
 
print ("Введите данные о дверных проемах (в метрах) ...")
h_d = int(input ("высота: "))
w_d = int(input ("ширина: "))
n = int(input ("количество: "))

Теперь создаем объект класса Room. Описание класса находится в модуле rooms, который был импортирован инструкцией import (а не from), поэтому, чтобы получить доступ к классу Room и его атрибутам, следует при создании объекта указать модуль, в котором он находится:

uroom = rooms.Room(l,w,h)

А теперь можно пользоваться атрибутами класса из модуля сколько влезет:

uroom.win_door(h_w, w_w, h_d, w_d, m,n)
uroom.wallpapers()
uroom.printer() 

Практическая работа:

  1. Создайте скрипт, импортирующий модуль с классом Room и использующий его (как показано в данном уроке).
  2. Допишите предыдущую программу, расширив ее возможности: можно по-желанию получить дополнительные сведения (площадь окна и двери).
  3. Переделайте программу таким образом, чтобы она не запрашивала у пользователя данные, а предлагала выбор из пяти готовых решений: на экран выводятся характеристики различных помещений, — пользователю остается только выбрать.

Вот мое решение покритикуйте,

Вот мое решение покритикуйте, плиз!!!

import rooms
 
class factori:
	def genereitRoomsInfo(self, l,w,h,h_d,w_d,h_w,w_w,n,m):
		uroom = rooms.Room(l,w,h)
		uroom.win_door(h_w, w_w, h_d, w_d, m,n)
		uroom.wallpapers()
		uroom.windowsSquare()
		uroom.doorsSquare()
		uroom.printer()	
 
 
print ("Выбирите № типа комнаты от 1 до 5: ")
print('1) 7х5х3, 1 дверь = 2х2, 1 окно = 2х3')
print('2) 4х4х3, 1 дверь = 2х1, 1 окно = 2х3')
print('3) 10х10х3, 2 двери = 2х2, 1 окно =2х4')
print('4) 8х2х3, 1 дверь = 2х1, 3 окна = 2х2')
print('5) 20х20х3, 5 дверь = 2х2, 5 окон = 2х3')
 
z=int (input('===> '))
 
f = factori()
if z == 1:
	f.genereitRoomsInfo(7,5,3,2,2,2,3,1,1)
elif z ==2:
	f.genereitRoomsInfo(4,4,3,2,1,2,3,1,1)
elif z ==3:
	f.genereitRoomsInfo(10,10,3,2,2,2,4,2,1)
elif z ==4:
	f.genereitRoomsInfo(8,2,3,2,1,2,1,1,3)
elif z ==5:
	f.genereitRoomsInfo(20,20,3,2,3,2,3,5,5)
else:
	print ('Некоректный ввод!!!!!!!')

Можно было бы использовать

Можно было бы использовать метод __init__ вместо genereitRoomsInfo. В таком случае создание экземпляра происходило бы внутри конструкции if-elif. Например:

if z == 1:
	f = factori(7,5,3,2,2,2,3,1,1)
...

Как минимум плюс в том, что объект не будет создан при некорректном вводе. Кроме того возможно так код быстрее выполняется.

"Первым делом, импортируем

"Первым делом, импортируем содержимое файла rooms.py"

а если импорт может и не пригодится, допустим есть ветвление в программе, где два варианта - расчет проводится и расчет не проводится? В середине кода import нельзя применять?

На сколько помню, можно.

На сколько помню, можно.

Есть вопрос по импорту. Как

Есть вопрос по импорту. Как вызывать методы, если они импортированы из разных файлов? то есть если из примера ниже вынести класс DOM в отдельный файл, то если заводим объект идет ругань

##файл a1room.py
class Radiator:
	def __init__(self, s, m = 'chugun', c = 'white'):
		self.section = s
		self.mat = m
		self.color = c
 
class one_rad(Radiator):
	def teplo(self):
		self.teplo_bat = self.section * 120
		if self.mat == 'stalnoy':
			self.teplo_bat = self.teplo_bat * 1.65
 
class room:
	def __init__(self, l, sh, cho):
		self.long = l
		self.shir = sh
		self.ch_okon = cho
		self.potera_okon(cho)
	def potera_okon(self, chn):
		if chn > 1:
			self.koef_poter = 1 + (chn * 0.5)
		else:
			self.koef_poter = 1
	def kol_bat(self, sek, mat='chugun'):
		self.s_room = self.long*self.shir
		self.bat_in_room = one_rad(sek, mat)
		self.teplo_bat = self.bat_in_room.teplo()
		self.kol_vo_bat = (self.s_room*110*self.koef_poter)/self.bat_in_room.teplo_bat
	def skolko(self):
		if round(self.kol_vo_bat,0) == 1:
			print ('nado', round(self.kol_vo_bat, 0), 'radiator')
		elif 1<round(self.kol_vo_bat,0)<5:
			print ('nado', round(self.kol_vo_bat, 0), 'radiatora')
		else:
			print ('nado', round(self.kol_vo_bat, 0), 'radiatorov')
 
##файл a2room.py
class DOM:
	def __init__(self, s):
		self.kom1 = room(s[0],s[1],s[2])
		self.kom2 = room(s[5],s[6],s[7])
		self.teplo_doma(s[3],s[4],s[8],s[9])
	def teplo_doma(self, n,m,nn,mm):
		self.kol_rad_in_room1=self.kom1.kol_bat(n,m)
		self.kol_rad_in_room2=self.kom2.kol_bat(nn,mm)
		self.kol_rad_in_room=round(self.kom1.kol_vo_bat,0)+round(self.kom2.kol_vo_bat,0)
	def skolko(self):
		if self.kom2.long == 0:
			self.kol_kom = 1
		else:
			self.kol_kom = 2
		if self.kol_rad_in_room == 1:
			print ('na', self.kol_kom, ' kom. nuzhno', self.kol_rad_in_room, 'radiator')
		elif 1<self.kol_rad_in_room<5:
			print ('na', self.kol_kom, ' kom. nuzhno', self.kol_rad_in_room, 'radiatora')
		else:
			print ('na', self.kol_kom, ' kom. nuzhno', self.kol_rad_in_room, 'radiatorov')
 
##заводим дом
>>> import a1room
>>> import a2room
>>> v1 = [2,3,1,7,'chugun', 2,3,1,7,'stalnoy']
>>> v2 = [2,3,1,7,'chugun', 2,3,1,7,'stalnoy']
>>> v3 = [2,3,1,7,'chugun', 2,3,1,7,'stalnoy']
>>> v4 = [2,3,1,7,'chugun', 2,3,1,7,'stalnoy']
>>> v5 = [2,3,1,7,'chugun', 2,3,1,7,'stalnoy']
>>> s = [v1,v2,v3,v4,v5]
 
>>> dom1 = a2room.DOM(s[2])
Traceback (most recent call last):
  File "<pyshell#139>", line 1, in <module>
    dom1 = a2room.DOM(s[2])
  File "E:\Python32\lib\a2room.py", line 3, in __init__
    self.kom1 = room(s[0],s[1],s[2])
NameError: global name 'room' is not defined
 
##строка
self.kom1 = a2room.room(s[0],s[1],s[2])
##в файле a2room.py ничего не дает

Как в таком случае организовываются ссылки?

Привет у тебя в файле

Привет
у тебя в файле a2room.py идёт обращение к классам которые находятся в a1room.py.
просто добавь в начало файла a2room.py строчку:

#файл a2room.py
from a1room import *
#...

Почти так же, как у Ra,

Почти так же, как у Ra, только на моем предыдущем решении и с использованием цикла for для вывода выриантов

##содержимое файла amydom.py, в нем основная программа, с прошлым уроком
##переделаны в классе DOM переменные на индексы:
class room:
	def __init__(self, l, sh, cho):
		self.long = l
		self.shir = sh
		self.ch_okon = cho
		self.potera_okon(cho)
	def potera_okon(self, chn):
		if chn > 1:
			self.koef_poter = 1 + (chn * 0.5)
		else:
			self.koef_poter = 1
	def kol_bat(self, sek, mat='chugun'):
		self.s_room = self.long*self.shir
		self.bat_in_room = one_rad(sek, mat)
		self.teplo_bat = self.bat_in_room.teplo()
		self.kol_vo_bat = (self.s_room*110*self.koef_poter)/self.bat_in_room.teplo_bat
	def skolko(self):
		if round(self.kol_vo_bat,0) == 1:
			print ('nado', round(self.kol_vo_bat, 0), 'radiator')
		elif 1<round(self.kol_vo_bat,0)<5:
			print ('nado', round(self.kol_vo_bat, 0), 'radiatora')
		else:
			print ('nado', round(self.kol_vo_bat, 0), 'radiatorov')
 
class DOM:
	def __init__(self, s):
		self.kom1 = room(s[0],s[1],s[2])
		self.kom2 = room(s[5],s[6],s[7])
		self.teplo_doma(s[3],s[4],s[8],s[9])
	def teplo_doma(self, n,m,nn,mm):
		self.kol_rad_in_room1=self.kom1.kol_bat(n,m)
		self.kol_rad_in_room2=self.kom2.kol_bat(nn,mm)
		self.kol_rad_in_room=round(self.kom1.kol_vo_bat,0)+round(self.kom2.kol_vo_bat,0)
	def skolko(self):
		if self.kom2.long == 0:
			self.kol_kom = 1
		else:
			self.kol_kom = 2
		if self.kol_rad_in_room == 1:
			print ('na', self.kol_kom, ' kom. nuzhno', self.kol_rad_in_room, 'radiator')
		elif 1<self.kol_rad_in_room<5:
			print ('na', self.kol_kom, ' kom. nuzhno', self.kol_rad_in_room, 'radiatora')
		else:
			print ('na', self.kol_kom, ' kom. nuzhno', self.kol_rad_in_room, 'radiatorov')
 
##файл amydqwe.py это интерфейс для вывода вариантов и его выбора:
import amydom
v1 = [2,3,1,7,'chugun', 2,3,1,7,'stalnoy']
v2 = [3,4,1,7,'chugun', 3,4,1,7,'stalnoy']
v3 = [4,6,2,7,'chugun', 4,6,2,8,'stalnoy']
v4 = [5,6,3,7,'chugun', 5,6,3,7,'stalnoy']
v5 = [5,6,3,8,'chugun', 5,6,3,8,'stalnoy']
s = [v1,v2,v3,v4,v5]
k=0
for i in s:
	print('var №',k+1,':', i)
	k = k+1
 
i=int(input('Выберите один из пяти вариантов : '))-1
dom1 = amydom.DOM(s[i])
dom1.skolko()
 
##ну и выполнение
>>> import amydqwe
var № 1 : [2, 3, 1, 7, 'chugun', 2, 3, 1, 7, 'stalnoy']
var № 2 : [3, 4, 1, 7, 'chugun', 3, 4, 1, 7, 'stalnoy']
var № 3 : [4, 6, 2, 7, 'chugun', 4, 6, 2, 8, 'stalnoy']
var № 4 : [5, 6, 3, 7, 'chugun', 5, 6, 3, 7, 'stalnoy']
var № 5 : [5, 6, 3, 8, 'chugun', 5, 6, 3, 8, 'stalnoy']
Выберите один из пяти вариантов : 4
na 2  kom. nuzhno 16.0 radiatorov
 
>>> ========== RESTART ============
>>> import amydqwe
var № 1 : [2, 3, 1, 7, 'chugun', 2, 3, 1, 7, 'stalnoy']
var № 2 : [3, 4, 1, 7, 'chugun', 3, 4, 1, 7, 'stalnoy']
var № 3 : [4, 6, 2, 7, 'chugun', 4, 6, 2, 8, 'stalnoy']
var № 4 : [5, 6, 3, 7, 'chugun', 5, 6, 3, 7, 'stalnoy']
var № 5 : [5, 6, 3, 8, 'chugun', 5, 6, 3, 8, 'stalnoy']
Выберите один из пяти вариантов : 5
na 2  kom. nuzhno 14.0 radiatorov
 
##да - нет в принципе как у Ra организовывается

моё решение практической

моё решение практической работы

#файл rooms.py
class Hole:
            def __init__(self,h):
                        self.square = h[0]*h[1]
                        self.numb=h[2]
 
class Win(Hole):
            pass
 
class Door(Hole):
            pass
 
class Room:
            def __init__(self,r):
                        self.square = 2*r[2]*(r[0]+r[1])
 
            def hole(self, w, d):
                        self.window = Win(w)
                        self.door = Door(d)
 
            def wallpapers(self):
                        self.wallpapers = self.square - \
                        self.window.square * self.window.numb- \
                        self.door.square * self.door.numb
 
            def printer(self):
                        print ("Площадь стен комнаты равна:",\
                        str(self.square),"кв.м")
                        print ("Оклеиваемая площадь равна:", \
                        str(self.wallpapers),"кв.м")
 
#файл lessn8.py
import rooms
room    = [[2,3,4], [1,3,4], [3,4,2], [4,5,6],  [7,7,7]]
windows = [[1,1,3], [1,2,2], [2,1,1], [1,1.5,1],[2,2,3]]
doors   = [[2,1,1], [1,3,1], [3,2,2], [1,1,1],  [2,2,3]]
i=0
while i<5:
            print('*****',i+1,'*****')
            print('Размер комнаты            ',room[i])
            print('Размер и количество окон  ',windows[i])
            print('Размер и количество дверей',doors[i])
            print()
            i+=1
 
i=int(input('Выберите один из пяти вариантов : '))-1
uroom = rooms.Room(room[i])
uroom.hole(windows[i], doors[i])
uroom.wallpapers()
uroom.printer()
print()
s=input('Вывести площадь окна и двери ? [Y/n] ')
if s=='' or s=='y' or s=='Y':
            print('Площадь одного окна равна',uroom.window.square,"кв.м")
            print('Площадь одной двери равна',uroom.door.square,"кв.м")
print()