Объектно-ориентированное программирование
Процедурный и объектно-ориентированный подход
Процедурный подход хорош для небольших (до 500 строк) проектов. Объектно-ориентированно программировать можно и на ассемблере. Но трудно. Удобнее программировать на языке, где есть для этого возможности.
Посмотрим, что есть в питоне для объектно-ориентированного подхода.
Рассмотрим окружности на плоскости ХУ.
Для задания окружности нужно задать координаты центра окружности x, y и ее радиус r. Для этого можно использовать обычный кортеж.
circle = (1, 2, 15)
Проблемы:
- не очевидно, где тут радиус. Может быть (x, y, r), а может мы задавали (r, x, y)
- если есть функции
distance_from_origin(x, y)
(расстояние от начала координат до центра окружности) иedge_distance_from_origin(x, y, radius)
(расстояние от начала координат до окружности), при обращении к ним нам нужно распаковывать кортеж (лишние операции):
Решение: именованный кортеж (named tuple):distance = distance_from_origin(*circle[:2]) distance = edge_distance_from_origin(*circle)
import collections
Circle = collections.namedtuple("Circle", "x y radius")
circle = Circle(13, 84, 9)
distance = distance_from_origin(circle.x, circle.y)
Проблемы:
- можно создать кортеж с отрицательным радиусом (нет проверки значений) (при процедурном подходе эти проверки либо не делаются, либо требуют написания слишком много кода);
- tuple неизменяем, named tuple изменяем через метод collections.namedtuple_replace() (но код ужасает)
circle = circle._replace(radius=12)
Решение: взять коллекцию с изменяемыми значениями, например, list или dict.
circle = [1, 2, 15] # list
circle = dict(x=36, y=77, radius=8) # dict
- list
- нет доступа к circle['x'],
- можно сделать circle.sort()
- dict - решает проблемы из list, но
- еще нет защиты от отрицательного радиуса,
- еще можно передать в функцию, которая не работает с окружностями (а, например, ждет прямоугольник).
Решение: сделать свой новый тип данных, который будет представлять окружность на ХУ плоскости.
Новые типы данных
Новый тип данных в питоне называется класс (class). Данные этого типа называются (как и раньше) объектами или экземплярами класса (instance).
- Класс - это набор правил (способ создания и работы с однотипными объектами):
- из каких переменных и данных состоит объект - атрибуты или поля - характеризуют состояние объекта;
- что можно делать с экземпляром класса (поведение объекта) - метод или функция класса - могут менять состояние объекта.
- конструктор - специальный метод, где описывается как создавать объект.
5 - это объект класса int. Для него определены арфиметические операции, его можно преобразовать в строку, его можно сделать из других объектов (строки, float). "Hello" - это объект класса str. Строки можно складывать, брать из них срезы, находить в ней подстроку, и так далее.
As a noun, "AT-trib-ute" is pronounced with emphasis on the first syllable, as opposed to "a-TRIB-ute", which is a verb. (Downey, p 144).
Создаем новый класс
- Для создания класса используют ключевое слово class
- поля и методы пишут с отступом
- имя класса принято писать с большой буквы
Класс записывается так (будем использовать сегодня):
class ИмяКласса:
'Описание для чего нужен класс - строка документации (можно не писать)'
поля и методы класса
или так (будем использовать, когда начнем говорить о наследовании):
class ИмяКласса(базовые классы через запятую):
'Описание для чего нужен класс - строка документации (можно не писать)'
поля и методы класса
На самом деле, когда базовый класс не указывается явно, это класс object
Самый простой класс (мы использовали для создания своего исключения):
class MyOwnException(Exception): # мой класс исключений наследуется от базового класса Exception
pass # ничего дополнительного или особого он не делает, просто существует
Пример класса, описывающего окружности:
from math import PI
class Circle:
'Окружности на плоскости ХУ'
# конструктор класса, вызывается когда создаем новый объект класса
def __init__(self, x=0, y=0, r=1):
self.x = x # все переменные ОБЪЕКТА указываются в конструкторе
self.y = y
self.r = r
# методы объекта:
def area(self): # первый аргумент всегда self
return PI * self.r * self.r # доступ к аргументам - только через self.
def perimetr(self): # первый аргумент всегда self
return 2*PI * self.r # доступ к аргументам - только через self.
def zoom(self, k): # увеличим окружность в k раз
self.r *= k
def is_crossed(self, c): # пересекается или нет эта окружность с окружностью с?
d2 = (self.x - c.x)**2 + (self.y - c.y)**2
r2 = (self.r + c.r)**2
return d2 <= r2
def __str__(self):
return 'Circle x={} у={} r={}, area={}'.format(self.x, self.y. self.r, self.area())
# обратите внимание на вызов self.area() - вызываем другой метод этого объекта тоже через self
# тут можно уже создавать объекты класса и их использовать
- __init__(self, другие аргументы через запятую) - конструктор класса (не совсем так, там еще есть __new__, но об этом подробнее в наследовании и переопределении методов).
- self - ссылка на себя, через нее доступаемся к атрибутам (полям и методам)
self.метод - вызов другого метода класса
self - первый аргумент методов экземпляра класса.
Создание объекта (экземпляра класса)
с = Circle(1, 2, 3)
- Создается экземпляр класса Circle - окружность с центром в точке (1, 2) и радиусом 3
- ссылка на нее записывается в переменную
c
Доступ к полям и методам
ссылканаобъект.поле
ссылканаобъект.метод
c = Circle()
c.x = 1
c.y = 2
c.r = 3
a = c.area() # у объекта, на который ссылается с, вызвали метод area
Объекты и ссылки (повторение - мать учения)
c = Circle() # создали единичную окружность с центром в (0,0), на нее ссылается с
c.x = 1 # эта окружность стала радиусом 3 с центром в (1, 2)
c.y = 2
c.r = 3
a = c.area() # у объекта, на который ссылается с, вызвали метод area
d = Circle(5, 6, 2.5) # создали окружность радиуса 2.5 с центром в (5, 6), на нее ссылается d
a = c.area() + d.area() # площадь окружности, на которую ссылается с и площадь окружности, на которую ссылается d
c = d # теперь c ТОЖЕ ссылается на вторую окружность, ссылок на первую окружность нет
c = d - это присвоение ссылок на объекты.
Что будет напечатано?
c = Circle()
d = Circle()
c.x = 1
d.x = 6
print('c.x = ', c.x)
print('d.x = ', d.x)
c = d
print('c.x = ', c.x)
print('d.x = ', d.x)
Функция возвращает объект
Method overloading
Можно ли создать в питоне методы класса с одинаковыми именами?
Нет. В python так не пишут.
Вы можете использовать значения по умолчанию, *args и **kargs - в питоне есть другие механизмы для того же результата.
Можно использовать декоратор @overload
, но о декораторах расскажем позже.
Термины (заключение)
Модель - упрощенное описание
Объект:
- Переменные (характеризуют состояние объекта)
- Методы (могут состояния менять)
Класс – способ задания однотипных объектов
- прототип объекта (его переменные)
- метод создания из прототипа конкретного объекта
Еще раз "на пальцах":
- Класс - шаблон объекта,
- Объект - экземпляр класса.
Термины на английском языке
Понадобятся при поиске в интернете.
https://www.tutorialspoint.com/python/python_classes_objects.htm
Class - A user-defined prototype for an object that defines a set of attributes that characterize any object of the class. The attributes are data members (class variables and instance variables) and methods, accessed via dot notation.
Class variable - A variable that is shared by all instances of a class. Class variables are defined within a class but outside any of the class's methods. Class variables are not used as frequently as instance variables are.
Data member - A class variable or instance variable that holds data associated with a class and its objects.
Function overloading - The assignment of more than one behavior to a particular function. The operation performed varies by the types of objects or arguments involved.
Instance variable - A variable that is defined inside a method and belongs only to the current instance of a class.
Inheritance - The transfer of the characteristics of a class to other classes that are derived from it.
Instance - An individual object of a certain class. An object obj that belongs to a class Circle, for example, is an instance of the class Circle.
Instantiation - The creation of an instance of a class.
Method - A special kind of function that is defined in a class definition.
Object - A unique instance of a data structure that's defined by its class. An object comprises both data members (class variables and instance variables) and methods.
Operator overloading - The assignment of more than one function to a particular operator.
Задачи
Uno card game (на дом)
Добавить карты +2, skip, wild, wild+4