Уроки Питона №10/13. Модули (import math) и пакеты в Питоне

Урок 13. Модули и пакеты

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

Модули в Python

Что такое модуль в Python?

Под модулем в Python понимается файл с расширением .py. Модули предназначены для того, чтобы в них хранить часто используемые функции, классы, константы и т.п. Можно условно разделить модули и программы: программы предназначены для непосредственного запуска, а модули для импортирования их в другие программы. Стоит заметить, что модули могут быть написаны не только на языке Python, но и на других языках (например C).

Как импортировать модули в Python?

Самый простой способ импортировать модуль в Python это воспользоваться конструкцией:

import имя_модуля

Импорт и использование модуля math, который содержит математические функции, будет выглядеть вот так.

>>> import math

>>> math.factorial(5)

120

За один раз можно импортировать сразу несколько модулей, для этого их нужно перечислить через запятую после слова import:

import имя_модуля1, имя_модуля2

>>> import math, datetime

>>> math.cos(math.pi/4)

0.707106781186547

>>> datetime.date(2017, 3, 21)

datetime.date(2017, 3, 21)

 

Если вы хотите задать псевдоним для модуля в вашей программе, можно воспользоваться вот таким синтаксисом:

import имя_модуля as новое_имя

>>> import math as m

>>> m.sin(m.pi/3)

0.866025403784438

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

from имя_модуля import имя_объекта

>>> from math import cos

>>> cos(3.14)

0.999998731727539

При этом импортируется только конкретный объект (в нашем примере: функция cos), остальные функции недоступны, даже если при их вызове указать имя модуля.

>>> from math import cos

>>> cos(3.14)

-0.999998731727539

>>> sin(3.14)

Traceback (most recent call last):

  File "<pyshell#2>", line 1, in <module>

    sin(3.14)

NameError: name 'sin' is not defined

>>> math.sin(3.14)

Traceback (most recent call last):

  File "<pyshell#3>", line 1, in <module>

    math.sin(3.14)

NameError: name 'math' is not defined

Для имортирования нескольких функций из модуля, можно перечислить их имена через запятую.

from имя_модуля import имя_объекта1, имя_объекта2

>>> from math import cos, sin, pi

>>> cos(pi/3)

0.500000000000000

>>> sin(pi/3)

0.866025403784438

Импортируемому объекту можно задать псевдоним.

from имя_модуля import имя_объекта as псевдоним_объекта

>>> from math import factorial as f

>>> f(4)

24

Если необходимо импортировать все фукнции, классы и т.п. из модуля, то воспользуйтесь следующей формой оператора from … import …

from имя_модуля import *

>>> from math import *

>>> cos(pi/2)

6.123233995736766e-17

>>> sin(pi/4)

0.707106781186547

>>> factorial(6)

720

Пакеты в Python

Что такое пакет в Python?

Пакет в Python – это каталог, включающий в себя другие каталоги и модули, но при этом дополнительно содержащий файл __init__.py. Пакеты используются для формирования пространства имен, что позволяет работать с модулями через указание уровня вложенности (через точку).

Для импортирования пакетов используется тот же синтаксис, что и для работы с модулями.

Использование пакетов в Python

Рассмотрим следующую структуру пакета:

fincalc

|-- __init__.py

|-- simper.py

|-- compper.py

|-- annuity.py

Пакет fincal содержит в себе модули для работы с простыми процентами (simper.py), сложными процентами (compper.py) и аннуитетами (annuity.py).

Для использования фукнции из модуля работы с простыми процентами, можно использовать один из следующих вариантов:

import fincalc.simper

fv = fincalc.simper.fv(pv, i, n)

import fincalc.simper as sp

fv =sp.fv(pv, i, n)

from fincalc import simper

fv = simper.fv(pv, i, n)

Файл __init__.py может быть пустым или может содержать переменную __all__, хранящую список модулей, который импортируется при загрузке через конструкцию

from имя_пакета import *

Например для нашего случая содержимое __init__.py может быть вот таким:

__all__ = ["simper", "compper", "annuity"]

P.S.

 

 

Python. Урок 14. Классы и объекты

Данный урок посвящен объектно-ориентированному программированию в Python. Разобраны такие темы как создание объектов и классов, работа с конструктором, наследование и полиморфизм в Python.

Основные понятия объектно-ориентированного программирования

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

Выделяют три основных “столпа” ООП- это инкапсуляция, наследование и полиморфизм.

Инкапсуляция

Под инкапсуляцией понимается сокрытие деталей реализации, данных и т.п. от внешней стороны. Например, можно определить класс “холодильник”, который будет содержать следующие данные: производитель, объем, количество камер хранения, потребляемая мощность и т.п., и методы: открыть/закрыть холодильник, включить/выключить, но при этом реализация того, как происходит непосредственно включение и выключение пользователю вашего класса не доступна, что позволяет ее менять без опасения, что это может отразиться на использующей класс «холодильник» программе. При этом класс становится новым типом данных в рамках разрабатываемой программы. Можно создавать переменные этого нового типа, такие переменные называются объекты.

Наследование

Под наследованием понимается возможность создания нового класса на базе существующего. Наследование предполагает наличие отношения “является” между классом наследником и классом родителем. При этом класс потомок будет содержать те же атрибуты и методы, что и базовый класс, но при этом его можно (и нужно) расширять через добавление новых методов и атрибутов.

Примером базового класса, демонстрирующего наследование, можно определить класс “автомобиль”, имеющий атрибуты: масса, мощность двигателя, объем топливного бака и методы: завести и заглушить. У такого класса может быть потомок – “грузовой автомобиль”, он будет содержать те же атрибуты и методы, что и класс “автомобиль”, и дополнительные свойства: количество осей, мощность компрессора и т.п..

Полиморфизм

Полиморфизм позволяет одинаково обращаться с объектами, имеющими однотипный интерфейс, независимо от внутренней реализации объекта. Например, с объектом класса “грузовой автомобиль” можно производить те же операции, что и с объектом класса “автомобиль”, т.к. первый является наследником второго, при этом обратное утверждение неверно (во всяком случае не всегда). Другими словами полиморфизм предполагает разную реализацию методов с одинаковыми именами. Это очень полезно при наследовании, когда в классе наследнике можно переопределить методы класса родителя.

Классы в Python

Создание классов и объектов

Создание класса в Python начинается с инструкции class. Вот так будет выглядеть минимальный класс.

class C:

pass

Класс состоит из объявления (инструкция class), имени класса (нашем случае это имя C) и тела класса, которое содержит атрибуты и методы (в нашем минимальном классе есть только одна инструкция pass).

Для того чтобы создать объект класса необходимо воспользоваться следующим синтаксисом:

имя_объекта = имя_класса()

Статические и динамические атрибуты класса

Как уже было сказано выше, класс может содержать атрибуты и методы. Атрибут может быть статическим и динамическим (уровня объекта класса). Суть в том, что для работы со статическим атрибутом, вам не нужно создавать экземпляр класса, а для работы с динамическим – нужно. Пример:

class Rectangle:

default_color = "green"

def __init__(self, width, height):

self.width = width

self.height = height

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

>>> Rectangle.default_color

'green'

width и height – это динамические атрибуты, при их создании было использовано ключевое слово self. Пока просто примите это как должное, более подробно про self будет рассказано ниже. Для доступа к width и height предварительно нужно создать объект класса Rectangle:

>>> rect = Rectangle(10, 20)

>>> rect.width

10

>>> rect.height

20

Если обратиться через класс, то получим ошибку:

>>> Rectangle.width

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: type object 'Rectangle' has no attribute 'width'

При этом, если вы обратитесь к статическому атрибуту через экземпляр класса, то все будет ОК, до тех пор, пока вы не попытаетесь его поменять. 

Проверим ещё раз значение атрибута default_color:

>>> Rectangle.default_color

'green'

Присвоим ему новое значение:

>>> Rectangle.default_color = "red"

>>> Rectangle.default_color

'red'

Создадим два объекта класса Rectangle и проверим, что default_color у них совпадает:

>>> r1 = Rectangle(1,2)

>>> r2 = Rectangle(10, 20)

>>> r1.default_color

'red'

>>> r2.default_color

'red'

Если поменять значение default_color через имя класса Rectangle, то все будет ожидаемо: у объектов r1 и r2 это значение изменится, но если поменять его через экземпляр класса, то у экземпляра будет создан атрибут с таким же именем как статический, а доступ к последнему будет потерян:

Меняем default_color через r1:

>>> r1.default_color = "blue"

>>> r1.default_color

'blue'

При этом у r2 остается значение статического атрибута:

>>> r2.default_color

'red'

>>> Rectangle.default_color

'red'

Вообще напрямую работать с атрибутами – не очень хорошая идея, лучше для этого использовать свойства.

Методы класса

Добавим к нашему классу метод. Метод – это функция, находящаяся внутри класса и выполняющая определенную работу.

Методы бывают статическими, классовыми (среднее между статическими и обычными) и уровня класса (будем их называть просто словом метод). Статический метод создается с декоратором @staticmethod, классовый – с декоратором @classmethod, первым аргументом в него передается cls, обычный метод создается без специального декоратора, ему первым аргументом передается self:

class MyClass:

@staticmethod

def ex_static_method():

print("static method")

@classmethod

def ex_class_method(cls):

print("class method")

def ex_method(self):

print("method")

Статический и классовый метод можно вызвать, не создавая экземпляр класса, для вызова ex_method() нужен объект:

>>> MyClass.ex_static_method()

static method

>>> MyClass.ex_class_method()

class method

>>> MyClass.ex_method()

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: ex_method() missing 1 required positional argument: 'self'

>>> m = MyClass()

>>> m.ex_method()

method

Конструктор класса и инициализация экземпляра класса

В Python разделяют конструктор класса и метод для инициализации экземпляра класса. Конструктор класса это метод __new__(cls, *args, **kwargs) для инициализации экземпляра класса используется метод __init__(self). При этом, как вы могли заметить __new__ – это классовый метод, а __init__ таким не является. Метод __new__ редко переопределяется, чаще используется реализация от базового класса object (см. раздел Наследование), __init__ же наоборот является очень удобным способом задать параметры объекта при его создании.

Создадим реализацию класса Rectangle с измененным конструктором и инициализатором, через который задается ширина и высота прямоугольника:

class Rectangle:

def __new__(cls, *args, **kwargs):

print("Hello from __new__")

return super().__new__(cls)

def __init__(self, width, height):

print("Hello from __init__")

self.width = width

self.height = height

>>> rect = Rectangle(10, 20)

Hello from __new__

Hello from __init__

>>> rect.width

10

>>> rect.height

20

Что такое self?

До этого момента вы уже успели познакомиться с ключевым словом selfself – это ссылка на текущий экземпляр класса, в таких языках как JavaC# аналогом является ключевое слово this. Через self вы получаете доступ к атрибутам и методам класса внутри него:

class Rectangle:

def __init__(self, width, height):

self.width = width

self.height = height

def area(self):

return self.width * self.height

В приведенной реализации метод area получает доступ к атрибутам width и height для расчета площади. Если бы в качестве первого параметра не было указано self, то при попытке вызвать area программа была бы остановлена с ошибкой.

Уровни доступа атрибута и метода

Если вы знакомы с языками программирования JavaC#C++ то, наверное, уже задались вопросом: “а как управлять уровнем доступа?”. В перечисленных языка вы можете явно указать для переменной, что доступ к ней снаружи класса запрещен, это делается с помощью ключевых слов (privateprotected и т.д.). В Python таких возможностей нет, и любой может обратиться к атрибутам и методам вашего класса, если возникнет такая необходимость. Это существенный недостаток этого языка, т.к. нарушается один из ключевых принципов ООП – инкапсуляция. Хорошим тоном считается, что для чтения/изменения какого-то атрибута должны использоваться специальные методы, которые называются getter/setter, их можно реализовать, но ничего не помешает изменить атрибут напрямую. При этом есть соглашение, что метод или атрибут, который начинается с нижнего подчеркивания, является скрытым, и снаружи класса трогать его не нужно (хотя сделать это можно).

Внесем соответствующие изменения в класс Rectangle:

class Rectangle:

def __init__(self, width, height):

self._width = width

self._height = height

def get_width(self):

return self._width

def set_width(self, w):

self._width = w

def get_height(self):

return self._height

def set_height(self, h):

self._height = h

def area(self):

return self._width * self._height

В приведенном примере для доступа к _width и _height используются специальные методы, но ничего не мешает вам обратиться к ним (атрибутам) напрямую.

>>> rect = Rectangle(10, 20)

>>> rect.get_width()

10

>>> rect._width

10

Если же атрибут или метод начинается с двух подчеркиваний, то тут напрямую вы к нему уже не обратитесь (простым образом). Модифицируем наш класс Rectangle:

class Rectangle:

def __init__(self, width, height):

self.__width = width

self.__height = height

def get_width(self):

return self.__width

def set_width(self, w):

self.__width = w

def get_height(self):

return self.__height

def set_height(self, h):

self.__height = h

def area(self):

return self.__width * self.__height

Попытка обратиться к __width напрямую вызовет ошибку, нужно работать только через get_width():

>>> rect = Rectangle(10, 20)

>>> rect.__width

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'Rectangle' object has no attribute '__width'

>>> rect.get_width()

10

Но на самом деле это сделать можно, просто этот атрибут теперь для внешнего использования носит название: _Rectangle__width:

>>> rect._Rectangle__width

10

>>> rect._Rectangle__width = 20

>>> rect.get_width()

20

Свойства

Свойством называется такой метод класса, работа с которым подобна работе с атрибутом. Для объявления метода свойством необходимо использовать декоратор @property.

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

Сделаем реализацию класса Rectangle с использованием свойств:

class Rectangle:

def __init__(self, width, height):

self.__width = width

self.__height = height

@property

def width(self):

return self.__width

@width.setter

def width(self, w):

if w > 0:

self.__width = w

else:

raise ValueError

@property

def height(self):

return self.__height

@height.setter

def height(self, h):

if h > 0:

self.__height = h

else:

raise ValueError

def area(self):

return self.__width * self.__height

Теперь работать с width и height можно так, как будто они являются атрибутами:

>>> rect = Rectangle(10, 20)

>>> rect.width

10

>>> rect.height

20

Можно не только читать, но и задавать новые значения свойствам:

>>> rect.width = 50

>>> rect.width

50

>>> rect.height = 70

>>> rect.height

70

Если вы обратили внимание: в setter’ах этих свойств осуществляется проверка входных значений, если значение меньше нуля, то будет выброшено исключение ValueError:

>>> rect.width = -10

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

File "test.py", line 28, in width

raise ValueError

ValueError

Наследование

В организации наследования участвуют как минимум два класса: класс родитель и класс потомок. При этом возможно множественное наследование, в этом случае у класса потомка может быть несколько родителей. Не все языки программирования поддерживают множественное наследование, но в Python можно его использовать. По умолчанию все классы в Python являются наследниками от object, явно этот факт указывать не нужно.

Синтаксически создание класса с указанием его родителя выглядит так:

class имя_класса(имя_родителя1, [имя_родителя2,…, имя_родителя_n])

Переработаем наш пример так, чтобы в нем присутствовало наследование:

class Figure:

def __init__(self, color):

self.__color = color

@property

def color(self):

return self.__color

@color.setter

def color(self, c):

self.__color = c

class Rectangle(Figure):

def __init__(self, width, height, color):

super().__init__(color)

self.__width = width

self.__height = height

@property

def width(self):

return self.__width

@width.setter

def width(self, w):

if w > 0:

self.__width = w

else:

raise ValueError

@property

def height(self):

return self.__height

@height.setter

def height(self, h):

if h > 0:

self.__height = h

else:

raise ValueError

def area(self):

return self.__width * self.__height

Родительским классом является Figure, который при инициализации принимает цвет фигуры и предоставляет его через свойства. Rectangle – класс наследник от Figure. Обратите внимание на его метод __init__: в нем первым делом вызывается конструктор (хотя это не совсем верно, но будем говорить так) его родительского класса:

super().__init__(color)

super – это ключевое слово, которое используется для обращения к родительскому классу.

Теперь у объекта класса Rectangle помимо уже знакомых свойств width и height появилось свойство color:

>>> rect = Rectangle(10, 20, "green")

>>> rect.width

10

>>> rect.height

20

>>> rect.color

'green'

>>> rect.color = "red"

>>> rect.color

'red'

Полиморфизм

Как уже было сказано во введении в рамках ООП полиморфизм, как правило, используется с позиции переопределения методов базового класса в классе наследнике. Проще всего это рассмотреть на примере. Добавим в наш базовый класс метод info(), который печатает сводную информацию по объекту класса Figure и переопределим этот метод в классе Rectangle, добавим  в него дополнительные данные:

class Figure:

def __init__(self, color):

self.__color = color

@property

def color(self):

return self.__color

@color.setter

def color(self, c):

self.__color = c

def info(self):

print("Figure")

print("Color: " + self.__color)

class Rectangle(Figure):

def __init__(self, width, height, color):

super().__init__(color)

self.__width = width

self.__height = height

@property

def width(self):

return self.__width

@width.setter

def width(self, w):

if w > 0:

self.__width = w

else:

raise ValueError

@property

def height(self):

return self.__height

@height.setter

def height(self, h):

if h > 0:

self.__height = h

else:

raise ValueError

def info(self):

print("Rectangle")

print("Color: " + self.color)

print("Width: " + str(self.width))

print("Height: " + str(self.height))

print("Area: " + str(self.area()))

def area(self):

return self.__width * self.__height

Посмотрим, как это работает

>>> fig = Figure("orange")

>>> fig.info()

Figure

Color: orange

>>> rect = Rectangle(10, 20, "green")

>>> rect.info()

Rectangle

Color: green

Width: 10

Height: 20

Area: 200

Таким образом, класс наследник может расширять функционал класса родителя.

Популярные репетиторы:

Рейтинг 5 из 5: 45 отзывов
 
Когда еще учился в аспирантуре, я мечтал собрать в одно целое пару моих основных интересов: Математику, Информатику и Обучение, c самого истока своей карьеры.

Высококлассный математик для студентов и школьников, кандидат физико математических наук, докторант, педагогический стаж более 17 лет, поспешно   подготовит без посредников к вступительному экзамену ЕГЭ по математике в 9 класс с помощью современных схем по улучшению памяти и   умственной работы . Помощь в оформлении конспектов.

Впечатляюще поработал в стартапе по Машинному обучению и Data Mining. Консультации по математическим программам Microsoft Mathematics, MathLab и Maxima . Без усилий программирует на R, Scala и Java. Участвует в ведущих научных симпозиумах KDD, ACL и CVPR .

Более 320 учащихся  поступили «на бюджет» в ВУЗы Москвы: ВШЭ, МЭИ, МГУ и ФИ и т.д.. Занятия проводятся Локально в Москве м. Китай-город и по Skype. Опыт репетитора по высшей математике для абитуриентов более 20 лет. 他說中國.

Запись на занятия

Ваше сообщение отправлено