Объекты в Java
Разберём детально, что представляет собой объект в Java. Если оглянуться вокруг, мы найдём много объектов вокруг нас: автомобили, собаки, люди. Все они имеют состояние и поведение. Если мы рассмотрим автомобиль, то его состояние – марка, цвет, стоимость и поведение – передвигается по дорогам, требует ремонта.
Если вы сравниваете виртуальный объект с настоящим объектом, они имеют очень похожие характеристики. Виртуальные объекты также имеют состояние и поведение. Состояние виртуального объекта сохраняется в полях, а поведение отображается с помощью методов.
Получается, при разработке программного обеспечения методы работают с внутренним состоянием объекта, а связь между объектами осуществляется с помощью методов.
12.3. Атрибуты¶
Как и объекты реального мира, экземпляры классов обладают свойствами и поведением. Свойства определяются элементами-данными, которые содержит объект.
Можно добавить новые элементы-данные к экземпляру класса с помощью точечной нотации:
>>> p.x = 3 >>> p.y = 4
Этот синтаксис подобен синтаксису для обращения к переменной или функции модуля, например, math.pi или string.uppercase. И модули, и экземпляры класса создают свое собственное пространство имен, и синтаксис для доступа к элементам тех и других — атрибутам — один и тот же. В данном случае атрибуты, к которым мы обращаемся, — элементы-данные в экземпляре класса.
Следующая диаграмма состояний показывает результат выполненных присваиваний:
Переменная p ссылается на объект класса Point, который содержит два атрибута. Каждый из атрибутов ссылается на число.
Тот же самый синтаксис используется для получения значений атрибутов:
>>> print p.y 4 >>> x = p.x >>> print x 3
Выражение p.x означает: возьмите объект, на который указывает переменная p, затем возьмите значение атрибута x этого объекта. В приведенном примере мы присваиваем полученное значение переменной с именем x. Переменная x и атрибут x не вступают в конфликт имен, поскольку принадлежат разным пространствам имен.
Точечную нотацию можно использовать как часть любого выражения, так что следующие предложения совершенно типичны:
print '(%d, %d)' % (p.x, p.y) distance_squared = p.x * p.x + p.y * p.y
Конструкторы
Класс в Kotlin может иметь основной конструктор (primary constructor) и один или более дополнительных конструкторов (secondary constructors).
Основной конструктор является частью заголовка класса, его объявление идёт сразу после имени класса (и необязательных параметров).
Если у основного конструктора нет аннотаций и модификаторов видимости, ключевое слово может быть опущено.
Основной конструктор не может содержать в себе исполняемого кода. Инициализирующий код может быть помещён в соответствующие блоки (initializers blocks),
которые помечаются словом .
При создании экземпляра класса блоки инициализации выполняются в том порядке, в котором они идут в теле класса, чередуясь с инициализацией свойств.
Обратите внимание, что параметры основного конструктора могут быть использованы в инициализирующем блоке.
Они также могут быть использованы при инициализации свойств в теле класса. Для объявления и инициализации свойств основного конструктора в Kotlin есть лаконичное синтаксическое решение:
Для объявления и инициализации свойств основного конструктора в Kotlin есть лаконичное синтаксическое решение:
Такие объявления также могут включать в себя значения свойств класса по умолчанию.
Вы можете использовать при объявлении свойств класса.
Свойства, объявленные в основном конструкторе, могут быть изменяемые () и неизменяемые ().
Если у конструктора есть аннотации или модификаторы видимости, ключевое слово обязательно, и модификаторы используются перед ним.
Для более подробной информации см. «».
Дополнительные конструкторы
В классах также могут быть объявлены дополнительные конструкторы (secondary constructors), перед которыми используется ключевое слово .
Если у класса есть основной конструктор, каждый дополнительный конструктор должен прямо или косвенно ссылаться (через другой(ие) конструктор(ы)) на основной.
Осуществляется это при помощи ключевого слова .
Обратите внимание, что код в блоках инициализации фактически становится частью основного конструктора.
Дополнительный конструктор ссылается на основной при помощи своего первого оператора, поэтому код во всех блоках инициализации,
а также инициализация свойств выполняется перед выполнением кода в теле дополнительного конструктора. Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также
Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также.
Если в абстрактном классе не объявлено никаких конструкторов (основного или дополнительных), у этого класса автоматически сгенерируется пустой конструктор без параметров.
Видимость этого конструктора будет public.
Если вы не желаете иметь класс с открытым public конструктором, вам необходимо объявить пустой конструктор с соответствующим модификатором видимости.
Создание класса
Что имеет автомобиль? В частности, это:
- марка;
- цвет;
- мощность (в л/с);
- максимальная скорость (км/ч);
- объём бака (л);
- расход топлива (л) на 100 км пути.
Напишем класс Car (автомобиль) на C# (аналогично на Java):
C#
public class Car
{
private string brand;
private string color;
private int power;
private int maxSpeed;
private int volumeOfTank;
private double fuelConsumption;
}
1 |
publicclassCar { privatestringbrand; privatestringcolor; privateintpower; privateintmaxSpeed; privateintvolumeOfTank; privatedoublefuelConsumption; } |
Как вы могли заменить класс объявляется так: , ключевое слово class и имя класса. Тело класса определяется фигурными скобками. Внутри класса объявлены его поля.
Следует понимать, что класс — это каркас, иначе говоря, описание реального объекта. На основе этого «описания» создаются экземпляры реального объекта. Логично предположить, что необходим механизм для присваивания значениям полей характеристик объекта. Для этого существуют конструкторы класса.
Конструктор класса
Конструктор класса — это специальный метод, который вызывается при создании нового объекта и используется для инициализации полей класса значениями, а также для начальных вычислений, если они необходимы. После создания объекта конструктор вызвать нельзя. Кроме того, данный метод никогда не возвращает никакого значения.
Напишем конструктор для инициализации полей в классе Car:
C#
public class Car
{
private string brand;
private string color;
private int power;
private int maxSpeed;
private int volumeOfTank;
private double fuelConsumption;
//конструктор класса
public Car(string newBrand, string newColor, int newPower, int newMaxSpeed,
int newVolumeOfTank, double newFuelConsumption)
{
brand = newBrand;
color = newColor;
power = newPower;
maxSpeed = newMaxSpeed;
volumeOfTank = newVolumeOfTank;
fuelConsumption = newFuelConsumption;
}
}
1 |
publicclassCar { privatestringbrand; privatestringcolor; privateintpower; privateintmaxSpeed; privateintvolumeOfTank; privatedoublefuelConsumption; //конструктор класса publicCar(stringnewBrand,stringnewColor,intnewPower,intnewMaxSpeed, intnewVolumeOfTank,doublenewFuelConsumption) { brand=newBrand; color=newColor; power=newPower; maxSpeed=newMaxSpeed; volumeOfTank=newVolumeOfTank; fuelConsumption=newFuelConsumption; } } |
Конструктор объявляется так: public Имя (). Наличие параметров не обязательно. Соответственно выделяют конструкторы класса:
- без параметров
- с параметрами
Модификатор доступа обязательно public, поскольку конструктор всегда вызывается вне класса.
Конструктор по умолчанию — это пустой конструктор без параметров. Он всегда присутствует в классе (если нет других конструкторов), даже если он не был объявлен явно. Конструктор по умолчанию вызывается автоматически всегда, когда отсутствуют другие конструкторы. Его код это (писать не обязательно):
C#
public Car()
{
}
1 |
publicCar() { |
Класс может содержать несколько конструкторов с разными параметрами. При создании объекта будет вызван тот, который подходит по параметрам.
P.S. Ничего не запрещает написать в классе одновременно конструктор без параметров (явно; тогда им можно будет воспользоваться при создании нового экземпляра класса) и конструктор с параметрами.
12.12. Снова чистые функции¶
Вот черновая версия функции add_time:
def add_time(t1, t2): sum = Time() sum.hours = t1.hours + t2.hours sum.minutes = t1.minutes + t2.minutes sum.seconds = t1.seconds + t2.seconds return sum
Функция создает новый объект Time, инициализирует его атрибуты, и возвращает ссылку на него. Это — чистая функция, поскольку она не изменяет ни один из переданных ей объектов и не имеет побочных эффектов, вроде вывода значения на печать или получения ввода от пользователя.
Вот пример использования этой функции. Мы создадим два объекта Time: current_time, содержащий текущее время, и bread_time, содержащий количество времени, необходимое хлебопечке для приготовления хлеба. Затем воспользуемся функцией add_time чтобы узнать, во сколько хлеб будет готов.
>>> current_time = Time() >>> current_time.hours = 9 >>> current_time.minutes = 14 >>> current_time.seconds = 30 >>> bread_time = Time() >>> bread_time.hours = 3 >>> bread_time.minutes = 35 >>> bread_time.seconds = >>> done_time = add_time(current_time, bread_time)
Определим функцию print_time для вывода объекта Time, воспользовавшись оператором форматирования сток:
def print_time(time): print "%02i%02i%02i" % (time.hours, time.minutes, time.seconds)
Теперь выведем полученный нами результат:
>>> print_time(done_time) 12:49:30
Программа выводит 12:49:30, и это правильный результат. Однако, в некоторых случаях результат работы функции add_time будет неверным. Можете сами привести пример такого случая?
Проблема с функцией add_time в том, что функция не учитывает случаи, когда сумма секунд или минут превышает 60. Когда это случается, необходимо выполнить перенос из переполнившегося разряда в разряд минут или часов.
Вот вторая, улучшенная, версия нашей функции:
def add_time(t1, t2): sum = Time() sum.hours = t1.hours + t2.hours sum.minutes = t1.minutes + t2.minutes sum.seconds = t1.seconds + t2.seconds if sum.seconds >= 60 sum.seconds = sum.seconds - 60 sum.minutes = sum.minutes + 1 if sum.minutes >= 60 sum.minutes = sum.minutes - 60 sum.hours = sum.hours + 1 return sum
Создание классов и объектов
Создание класса в 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
Пример объявления класса
Допустим, у нас есть блог, на котором пользователи могут публиковать различные посты. Для создания большого количества однотипных объектов, в данном случае постов, следует написать заготовку или иными словами класс. Далее используя его, мы можем сделать экземпляры постов, передавая на вход для их создания определённые данные. После этого посты отобразятся на странице. Каждый экземпляр поста будет иметь свои собственные свойства, такие как текст и количество лайков. Кроме этого, каждый из них будет наследовать метод , с помощью которого мы будем увеличивать количество лайков конкретного поста.
Таким образом с помощью одного класса (одной заготовки) мы можем создать много однотипных объектов. При этом они будут независимы друг от друга и иметь как свои свойства и методы, так и наследуемые.
Создание класса начинается с ключевого слова после которого идёт название класса, написанное в нотации PascalCase (первая буква заглавная). Далее между фигурными скобками находится всё что касается этого класса, то есть свойства и методы.
Пример создания класса :
В этом классе находятся 2 метода: и . Синтаксис методов очень простой: сначала указывается название метода, далее в круглых скобках при необходимости параметры, а затем его тело в фигурных скобках.
– это специальный метод, который вызывается только при создании новых объектов, в данном случае экземпляров класса . В этом методе описываются свойства и методы, которые необходимо непосредственно добавить в создаваемый объект.
В данном примере у конструктора имеется один параметр . При вызове мы можем передать значение этому параметру, то есть аналогично тому, как мы это делаем при вызове функций.
В конструкторе имеется переменная . Это специальная переменная, которая ссылается на создаваемый объект. То есть, когда вы будете создавать новый экземпляр класса, автоматически будет указывать на него. С помощью мы добавляем к этому объекту сначала свойство со значением, которое мы передали в вызов . После этого ещё одно свойство но уже со значением .
– это метод, посредством которого мы будем увеличивать количество лайков у конкретного объекта типа . находится не в самих объектах, а на уровне класса , то есть в . Все создаваемые объекты будут иметь доступ к нему посредством наследования.
Почему так? Потому что мы описали вне конструктора. А все методы, описанные вне его, будут находиться на уровне класса, то есть в свойстве .
Откуда взялось это свойство ? Это свойство появляется автоматически при объявлении класса. Это прототип, который в дальнейшем будет устанавливаться для всех объектов, создаваемых посредством этого класса. Используются прототипы для наследования. То есть для того чтобы созданные объекты имели доступ к свойствам и методам, расположенных в прототипах.
В данном примере объект имеет 2 метода: и .
Свойство в прототипе содержит сам класс :
Итак, мы создали класс . У него имеется 2 метода: и . Конструктор вызывается только при создании нового объекта. Метод будет наследоваться и доступен для каждого экземпляра класса .
Объектно-ориентированное программирование
Объектно-ориентированный подход к разработке ПО был призван стать надежной заменой для структурной методологии программирования. Согласно этой уже устаревшей концепции, каждая отдельно взятая программа является иерархической структурой из функциональных блоков кода.
В свою очередь, ООП предлагает несколько иной способ реализации программ, представляя их в виде совокупности объектов, взаимодействующих между собой.
Благодаря такой особенности:
- Улучшается восприятие поставленной задачи при работе над проектом;
- Сокращается количество строк кода;
- Уменьшается сложность написания кода.
Основными принципами ООП являются следующие механизмы: абстракция, инкапсуляция, наследование и полиморфизм. Для создания программ, обрабатывающих информацию в виде объектов, необходимо понимание, а также комплексное соблюдение всех четырех парадигм. Практическое применение каждой из них можно встретить в примерах из данной статьи.
Рассмотрим основные принципы ООП:
- Абстракция выполняет основную задачу ООП, позволяя программисту формировать объекты определенного типа с различными характеристиками и поведением, благодаря применению классов.
- Инкапсуляция помогает скрыть детали реализации конкретных объектов и защитить их свойства от постороннего вмешательства.
- Наследование дает возможность расширять уже существующие классы за счет автоматического переноса их параметров и методов к новым структурам данных.
- Полиморфизм используется для создания единого интерфейса, который имеет множество различных реализаций, зависящих от класса применяемого объекта.
Работа с классами на Python
Большая часть времени работы программиста — это работа с классами и их экземплярами. Изменим наш предыдущий класс и добавим дополнительные атрибуты, которые сможем в последующем менять при работе с экземплярами класса.
class Car():
«»»Описание автомобиля»»»
def __init__(self, brand, model, years):
«»»Инициализирует атрибуты»»»
self.brand = brand
self.model = model
self.years = years
self.mileage = 0
def get_full_name(self):
«»»Автомобиль»»»
name = «Автомобиль {self.brand} {self.model} {self.years}»
name.
def read_mileage(self):
«»»Пробег автомобиля»»»
(«Пробег автомобиля {self.mileage} км.»)
В описание автомобиля есть три атрибута(параметра) это brand, model, years. Также мы создали новый атрибут mileage (пробег) и присвоили ему начальное значение 0. Так как пробег у всех автомобилей разный, в последующем мы сможем изменять этот атрибут. Метод get_full_name будет возвращать полное описание автомобиля. Метод read_mileage будет выводить пробег автомобиля.
Создадим экземпляр с классом Car и запустим методы:
car_2 = Car(‘audi’, ‘a4’, 2019)
print(car_2.get_full_name())
car_2.read_mileage()
В результате в начале Python вызывает метот __init__() для создания нового экземпляра. Сохраняет название, модель, год выпуска и создает новый атрибут с пробегом равным 0. В итоге мы получим такой результат:
Автомобиль Audi A4 2019
Пробег автомобиля 0 км.
2.1. Прямое изменение значения атрибута
Для изменения значения атрибута можно обратиться к нему напрямую и присвоить ему новое значение. Изменим пробег автомобиля car_2:
car_2 = Car(‘audi’, ‘a4’, 2019)
print(car_2.get_full_name())
car_2.mileage = 38
car_2.read_mileage()
Мы обратились к нашему экземпляру car_2 и связанным с ним атрибутом пробега(mileage) и присвоили новое значение 38. Затем вызвали метод read_mileage() для проверки. В результате мы получим следующие данные.
Автомобиль Audi A4 2019
Пробег автомобиля 38 км.
2.2. Изменение значения атрибута с использованием метода
В Python удобнее писать методы, которые будут изменять атрибуты за вас. Для этого вы просто передаете новое значение методу, который обновит значения. Добавим в наш класс метод который будет изменять показания пробега.
class Car():
«»»Описание автомобиля»»»
def __init__(self, brand, model, years):
«»»Инициализирует атрибуты»»»
self.brand = brand
self.model = model
self.years = years
self.mileage = 0
def get_full_name(self):
«»»Автомобиль»»»
name = «Автомобиль {self.brand} {self.model} {self.years}»
name.
def read_mileage(self):
«»»Пробег автомобиля»»»
(«Пробег автомобиля {self.mileage} км.»)
def update_mileage(self, new_mileage):
«»»Устанавливает новое значение пробега»»»
self.mileage = new_mileage
car_2 = Car(‘audi’, ‘a4’, 2019)
print(car_2.get_full_name())
car_2.read_mileage()
car_2.update_mileage(17100)
car_2.read_mileage()
Вначале выведем текущие показания пробега ( car_2.read_mileage() ). Затем вызовем метод и передадим ему новое значение пробега ( car_2.update_mileage(17100) ). Этот метод устанавливает пробег 17100. Выведем текущие показания ( car_2.read_mileage() ) и у нас получается:
Автомобиль Audi A4 2019
Пробег автомобиля 0 км.
Пробег автомобиля 17100 км.
2.3. Изменение значения атрибута с приращением
Если вместо того, чтобы присвоить новое значение, требуется изменить с значение с приращением, то в этом случаем мы можем написать еще один метод, который будет просто прибавлять пробег к уже имеющемся показаниям. Для этого добавим метод add_mileage в класс :
def add_mileage(self, km):
«»»Добавляет пробег»»»
self.mileage += km
Новый метод add_mileage() получает пробег в км и добавлет его к self.mileage.
car_2.add_mileage(14687)
car_2.()
Пробег автомобиля 31787 км.
В итоге после вызова метода add_mileage() пробег автомобиля в экземпляре увеличится на 14687 км и станет равным 31787 км. Данный метод мы можем вызывать каждый раз при изменении пробега и передавать новые значение, на которое будет увеличивать основной пробег.
Типы членов
Помимо переменных-членов и функций-членов классы могут иметь типы-члены или вложенные типы (включая псевдонимы типов). В следующем примере мы создаем калькулятор, в котором мы можем быстро изменить тип используемого числа, если нам когда-нибудь это понадобится.
Вывод программы:
В таком контексте имя класса эффективно действует как пространство имен для вложенного типа. Изнутри класса нам нужно ссылаться на него только как на . Извне класса мы можем получить доступ к типу через .
Когда мы решим, что больше не соответствует нашим потребностям, и мы хотим использовать , нам нужно обновить только псевдоним типа, а не заменять каждое использование на .
Члены-псевдонимы типов упрощают сопровождение кода и могут уменьшить количество набора текста. Шаблоны классов, о которых мы поговорим позже, часто используют члены псевдонимы типов. Вы уже видели это как , где – это псевдоним для .
Вложенные типы не могут быть объявлены предварительно. Как правило, вложенные типы следует использовать только тогда, когда вложенный тип используется исключительно внутри этого класса
Обратите внимание: поскольку классы являются типами, можно вкладывать классы внутрь других классов – это нераспространено и обычно выполняется только опытными программистами
Принципы ООП
Объектно-ориентированный язык работает по следующим принципам:
- Все данные представляются объектами
- Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
- Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
- Каждый объект имеет тип
- Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Идеи/принципы объектно-ориентированного программирования:
- Наследование. Возможность выделять общие свойства и методы классов в один класс верхнего уровня (родительский). Классы, имеющие общего родителя, различаются между собой за счет включения в них различных дополнительных свойств и методов.
- Инкапсуляция. Свойства и методы класса делятся на доступные из вне (опубликованные) и недоступные (защищенные). Защищенные атрибуты нельзя изменить, находясь вне класса. Опубликованные же атрибуты также называют интерфейсом объекта, т. к. с их помощью с объектом можно взаимодействовать. По идеи, инкапсуляция призвана обеспечить надежность программы, т.к. изменить существенные для существования объекта атрибуты становится невозможно.
- Полиморфизм. Полиморфизм подразумевает замещение атрибутов, описанных ранее в других классах: имя атрибута остается прежним, а реализация уже другой. Полиморфизм позволяет специализировать (адаптировать) классы, оставляя при этом единый интерфейс взаимодействия.
Преимущества ООП
В связи со своими особенностями объектно-ориентированное программирование имеет ряд преимуществ перед структурным (и др.) программированием. Выделим некоторые из них:
- Использование одного и того же программного кода с разными данными. Классы позволяют создавать множество объектов, каждый из которых имеет собственные значения атрибутов. Нет потребности вводить множество переменных, т.к объекты получают в свое распоряжение индивидуальные так называемые пространства имен. Пространство имен конкретного объекта формируется на основе класса, от которого он был создан, а также от всех родительских классов данного класса. Объект можно представить как некую упаковку данных.
- Наследование и полиморфизм позволяют не писать новый код, а настраивать уже существующий, за счет добавления и переопределения атрибутов. Это ведет к сокращению объема исходного кода.
Особенность ООП
ООП позволяет сократить время на написание исходного кода, однако ООП всегда предполагает большую роль предварительного анализа предметной области, предварительного проектирования. От правильности решений на этом предварительном этапе зависит куда больше,чем от непосредственного написания исходного кода.
Особенности ООП в Python
По сравнению с другими распространенными языками программирования у Python можно выделить следующие особенности, связанные с объектно-ориентированным программированием:
- Любое данное (значение) — это объект. Число, строка, список, массив и др. — все является объектом. Бываю объекты встроенных классов (как те,что перечисленные в предыдущем предложении), а бывают объекты пользовательских классов (тех, что создает программист). Для единого механизма взаимодействия предусмотрены методы перегрузки операторов.
- Класс — это тоже объект с собственным пространством имен. Это нигде не было указано в данном цикле уроков. Однако это так. Поэтому правильнее было употреблять вместо слова «объект», слово «экземпляр». И говорить «экземпляр объекта», подразумевая под этим созданный на основе класса именно объект, и «экземпляр класса», имея ввиду сам класс как объект.
- Инкапсуляции в Python не уделяется особого внимания. В других языках программирования обычно нельзя получить напрямую доступ к свойству, описанному в классе. Для его изменения может быть предусмотрен специальный метод. В Python же это легко сделать, просто обратившись к свойству класса из вне. Несмотря на это в Python все-таки предусмотрены специальные способы ограничения доступа к переменным в классе.
Наследование классов
Вместо того, чтобы начинать с нуля, вы можете создать класс, выведя его из ранее существовавшего класса, перечислив родительский класс в скобках после имени нового класса.
Дочерний класс наследует атрибуты своего родительского класса, и вы можете использовать эти атрибуты, как если бы они были определены в дочернем классе. Дочерний класс также может переопределять элементы данных и методы родительского класса.
Синтаксис
Производные классы объявляются так же, как их родительский класс; однако список базовых классов для наследования дается после имени класса
class SubClassName (ParentClass1): 'Optional class documentation string' class_suite
Пример
#!/usr/bin/python class Parent: # define parent class parentAttr = 100 def __init__(self): print "Calling parent constructor" def parentMethod(self): print 'Calling parent method' def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print "Parent attribute :", Parent.parentAttr class Child(Parent): # define child class def __init__(self): print "Calling child constructor" def childMethod(self): print 'Calling child method' c = Child() # instance of child c.childMethod() # child calls its method c.parentMethod() # calls parent's method c.setAttr(200) # again call parent's method c.getAttr() # again call parent's method
Когда приведенный выше код выполняется, он дает следующий результат
Calling child constructor Calling child method Calling parent method Parent attribute : 200
Аналогичным образом вы можете управлять классом из нескольких родительских классов следующим образом:
class A: # define your class A ..... class B: # define your class B ..... class C(A, B): # subclass of A and B .....
Вы можете использовать функции issubclass () или isinstance (), чтобы проверить отношения двух классов и экземпляров.
- Issubclass ( к югу, вир) функция булева возвращает истину , если данный подкласс суб действительно подкласс суперкласса вир .
- Isinstance (объект, класс) Функция булева возвращает истину , если OBJ является экземпляром класса Class или является экземпляром подкласса класса
Правила объявления классов, операторов импорта и пакетов в исходном файле
В последней части этого раздела давайте рассмотрим правила декларации исходного файла
Эти правила в Java имеют важное значение при объявлении классов, операторов импорта и операторов пакета в исходном файле
- В исходном файле может быть только один публичный класс (public class).
- Исходный файл может иметь несколько «непубличных» классов.
- Название публичного класса должно совпадать с именем исходного файла, который должен иметь расширение .java в конце. Например: имя класса public class Employee{}, то исходный файл должен быть Employee.java.
- Если класс определен внутри пакета, то оператор пакет должно быть первым оператором в исходном файле.
- Если присутствуют операторы импорта, то они должны быть написаны между операторами пакета и объявлением класса. Если нет никаких операторов пакета, то оператор импорта должен быть первой строкой в исходном файле.
- Операторы импорта и пакета будут одинаково выполняться для всех классов, присутствующих в исходном файле. В Java не представляется возможным объявить различные операторы импорта и/или пакета к различным классам в исходном файле.
Классы имеют несколько уровней доступа и существуют различные типы классов: абстрактные классы (abstract class), конечные классы (final class) и т.д. Обо всем этом обсудим в уроке модификаторы доступа.
Помимо указанных выше типов классов, Java также имеет некоторые специальные классы, называемые внутренние (Inner class) и анонимные классы (Anonymous class).