🔵 Объектно-ориентированное Программирование в Python
Объектно-ориентированное программирование (ООП) — это парадигма программирования, где различные компоненты компьютерной программы моделируются на основе реальных объектов. Объект — это что-либо, у чего есть какие-либо характеристики и то, что может выполнить какую-либо функцию.
Содержание
Представьте сценарий, где вам нужно разработать болид Формулы-1 используя подход объектно-ориентированного программирования. Первое, что вам нужно сделать — это определить реальные объекты в настоящей гонке Формула-1. Какие аспекты в Формуле-1 обладают определенными характеристиками и могут выполнять ту или иную функцию?
Мы собрали ТОП Книг для Python программиста которые помогут быстро изучить язык программирования Python. Список книг: Книги по PythonОдин из очевидных ответов на этот вопрос —
- мощность двигателя;
- марка;
- модель;
- производитель, и т. д.
Соответственно, болид можно запустить, остановить, ускорить, и так далее. Гонщик может быть еще одним объектом в Формуле-1. Гонщик имеет национальность, возраст, пол, и так далее, кроме этого, он обладает таким функционалом, как управление болидом, рулевое управление, переключение передач.
Как и в этом примере, в объектно-ориентированном программировании мы создадим объекты, которые будут соответствовать реальным аспектам.
Стоит обратить внимание на то, что объектно-ориентированное программирование — не зависящая от языка программирования концепция. Это общая концепция программирования и большинство современных языков, такие как Java, C#, C++ и Python поддерживают объектно-ориентированное программирование.
В этой статье мы разберем подробную инструкцию объектно-ориентированного программирования в Python, но перед этим, рассмотрим некоторые преимущества и недостатки объектно-ориентированного программирования.
Преимущества и недостатки ООП Python
Рассмотрим несколько основных преимуществ объектно-ориентированного программирования:
- Объектно-ориентированное программирование подразумевает повторное использование. Компьютерная программа написанная в форме объектов и классов может быть использована снова в других проектах без повторения кода;
- Использование модулярного подхода в объектно-ориентированном программировании позволяет получить читаемый и гибкий код;
- В объектно-ориентированном программировании каждый класс имеет определенную задачу. Если ошибка возникнет в одной части кода, вы можете исправить ее локально, без необходимости вмешиваться в другие части кода;
- Инкапсуляция данных (которую мы рассмотрим дальше в статье) вносит дополнительный уровень безопасности в разрабатываемую программу с использованием объектно-ориентированного подхода;
Хотя объектно-ориентированное программирование обладает рядом преимуществ, оно также содержит определенные недостатки, некоторые из них находятся в списке ниже:
- Для создания объектов необходимо иметь подробное представление о разрабатываемом программном обеспечении;
- Не каждый аспект программного обеспечения является лучшим решением для реализации в качестве объекта. Для новичков может быть тяжело прочертить линию в золотой середине;
- С тем, как вы вносите все новые и новые классы в код, размер и сложность программы растет в геометрической прогрессии;
В следующем разделе мы рассмотрим ряд самых важных концепций объектно-ориентированного программирования.
Как и следует из названия, объектно-ориентированное программирование — это речь об объектах. Однако, перед тем как создать объект, нам нужно определить его класс.
Класс
Класс в объектно-ориентированном программировании выступает в роли чертежа для объекта. Класс можно рассматривать как карту дома. Вы можете понять, как выглядит дом, просто взглянув на его карту.
Cам по себе класс не представляет ничего. К примеру, нельзя сказать что карта является домом, она только объясняет как настоящий дом должен выглядеть.
Отношение между классом и объектом можно представить более наглядно, взглянув на отношение между машиной и Audi. Да, Audi – это машина. Однако, нет такой вещи, как просто машина. Машина — это абстрактная концепция, которую также реализуют в Toyota, Honda, Ferrari, и других компаниях.
Ключевое слово class
используется для создания класса в Python. Название класса следует за ключом class
, за которым следует двоеточие. Тело класса начинается с новой строки, с отступом на одну вкладку влево.
Давайте рассмотрим, как мы можем создать самый простой класс в Python. Взглянем на следующий код:
# Создаем класс Car class Car: # создаем атрибуты класса name = «c200″ make = «mercedez» model = 2008 # создаем методы класса def start(self): print («Заводим двигатель») def stop(self): print («Отключаем двигатель»)
# Создаем класс Car class Car:
# создаем атрибуты класса name = «c200» make = «mercedez» model = 2008
# создаем методы класса def start(self): print («Заводим двигатель»)
def stop(self): print («Отключаем двигатель») |
В примере выше мы создали класс под названием Car
с тремя атрибутами: имя name
, марка make
и модель model
. Наш класс также содержит два метода: start()
и stop()
.
Объекты
Раннее мы поняли, что класс предоставляет чертеж объекта. Однако, чтобы на самом деле использовать объекты и методы класса, вам нужно создать объект из этого класса. Существует несколько методов и атрибутов класса, которые можно использовать вне объекта, мы рассмотрим их в следующем разделе.
Сейчас просто запомните, что по умолчанию, нам нужно создать объект класса перед тем, как мы сможем начать использовать его методы и атрибуты.
Объект также называется экземпляром. Тем не менее, процесс создания объекта класса называется инициализация. В Python, чтобы создать объект класса, нам просто нужно вписать название класса, с последующими открывающимися и закрывающимися скобками.
Давайте создадим объект класса Car
, который мы создали в предыдущем разделе.
# Создаем объект класса Car под названием car_a car_a = Car() # Создаем объект класса Car под названием car_b car_b = Car()
# Создаем объект класса Car под названием car_a car_a = Car()
# Создаем объект класса Car под названием car_b car_b = Car() |
В этом скрипте мы создали два объекта класса Car: car_a
и car_b
. Чтобы узнать тип созданных нами объектов, мы можем использовать метод type
и передать ему названия наших объектов. Выполните следующий код:
В выдаче вы увидите:
Это говорит нам о том, что тип объекта car_b
– класс Car
.
На данный момент мы создали наш класс и соответствующие ему объекты. Теперь настало время получить доступ к атрибутам класса и вызвать метод класса при помощи объекта класса. Чтобы сделать это, вам нужно только вписать имя объекта, за которым следует оператор точка .
и название атрибута или метода, к которому вы хотите получить доступ или вызов, соответственно. Давайте рассмотрим следующий пример:
В этом скрипте мы вызываем метод start()
через объект car_b
. Выдача будет выглядеть следующим образом:
Заводим двигатель
Заводим двигатель |
Аналогично, вы можете получить доступ к атрибуту, пользуясь следующим синтаксисом:
Атрибуты класса
В предыдущей секции мы разобрались, как создавать объекты класса и как мы можем использовать эти объекты для получения доступа к атрибутам класса
В Python, каждый объект содержит определенные атрибуты по умолчанию и методы в дополнение к определенным пользователем атрибутами. Чтобы посмотреть на все атрибуты и методы объекта, используйте встроенную функцию под названием dir()
. Попробуем взглянуть на все атрибуты объекта car_b
, который мы создали в предыдущем разделе. Выполните следующий скрипт:
В выдаче вы увидите следующие атрибуты:
[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘make’, ‘model’, ‘name’, ‘start’, ‘stop’]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
[‘__class__’, ‘__delattr__’, ‘__dict__’, ‘__dir__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__init_subclass__’, ‘__le__’, ‘__lt__’, ‘__module__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘__weakref__’, ‘make’, ‘model’, ‘name’, ‘start’, ‘stop’] |
Эта встроенная функция очень полезна при изучении атрибутов и функций объекта, особенно при использовании через REPL.
Атрибуты класса против атрибутов экземпляров
Атрибуты могут быть наглядно отнесены к двум типам:
- атрибуты класса
- атрибуты экземпляров
Атрибуты класса делятся среди всех объектов класса, в то время как атрибуты экземпляров являются собственностью экземпляра.
Помните, что экземпляр — это просто альтернативное название объекта.
Атрибуты экземпляра объявляются внутри любого метода, в то время как атрибуты класса объявляются вне любого метода.
Следующий пример прояснит эту разницу:
class Car: # создаем атрибуты класса car_count = 0 # создаем методы класса def start(self, name, make, model): print(«Двигатель заведен») self.name = name self.make = make self.model = model Car.car_count += 1
class Car:
# создаем атрибуты класса car_count = 0
# создаем методы класса def start(self, name, make, model): print(«Двигатель заведен») self.name = name self.make = make self.model = model Car.car_count += 1 |
В указанном выше скрипте мы создаем класс Car
с одним атрибутом класса под названием car_count
и три атрибута экземпляра под названием name
, make
и model
. Класс содержит один метод start()
, который содержит наши три атрибута экземпляров. Значения атрибутов экземпляров переданы в качестве аргументов методу start()
. Внутри метода start
, атрибут car_count
увеличен на один.
Стоит упомянуть, что внутри метода, атрибуты экземпляра ссылаются при помощи ключевого слова self
, в то время как атрибуты класса ссылаются при помощи названия класса.
Давайте создадим объект класса Car
и вызовем метод start()
.
car_a = Car() car_a.start(«Corrola», «Toyota», 2015) print(car_a.name) print(car_a.car_count)
car_a = Car() car_a.start(«Corrola», «Toyota», 2015) print(car_a.name) print(car_a.car_count) |
В скрипте выше мы вывели название атрибута экземпляра и атрибута класса car_count
. В выдаче вы увидите, что атрибут car_count
будет иметь значение 1, как показано ниже:
Двигатель заведен Corrola 1
Двигатель заведен Corrola 1 |
Теперь создадим еще один объект класса Car
и вызываем метод start()
.
car_b = Car() car_b.start(«City», «Honda», 2013) print(car_b.name) print(car_b.car_count)
car_b = Car() car_b.start(«City», «Honda», 2013) print(car_b.name) print(car_b.car_count) |
Сейчас если вы выведите значение атрибута car_count
, вы увидите 2 в выдаче. Это связано с тем, что атрибут car_count
является атрибутом класса и таким образом он разделяется между экземплярами. Объект car_a
увеличил свое значение до 1, в то время как car_b
увеличил свое значение еще раз, так что итоговое значение равняется 2. Выдача выглядит следующим образом:
Двигатель заведен City 2
Двигатель заведен City 2 |
Методы
Как мы выяснили ранее, в объектно-ориентированном программировании, методы используются для реализации функционалов объекта. В предыдущем разделе мы создали методы start()
и stop()
для класса Car
. До этих пор, мы использовали объекты класса для вызова методов. Однако, есть тип методов, который может быть вызван напрямую при помощи имени класса. Такой метод называется статичным методом.
Статичные методы
Для объявления статического метода, вам нужно указать дескриптор @staticmethod
перед названием метода, как показано ниже:
class Car: @staticmethod def get_class_details(): print («Это класс Car») Car.get_class_details()
class Car:
@staticmethod def get_class_details(): print («Это класс Car»)
Car.get_class_details() |
В коде выше мы создали класс Car
с одним статичным методом get_class_details()
. Давайте вызовем этот метод, используя название класса.
Вы можете видеть что нам не нужно создавать экземпляр класса Car
для вызова метода get_class_details()
, вместо этого мы просто использовали название класса. Стоит упомянуть, что статические методы могут иметь доступ только к атрибутам класса в Python, вы не сможете обратиться к методам через self
.
Возврат множественных значений из метода
Одна из лучших особенностей языка Python заключается в том, что методы класса могут возвращать множественные значения. Взгляните на следующий пример:
class Square: @staticmethod def get_squares(a, b): return a*a, b*b print(Square.get_squares(3, 5))
class Square:
@staticmethod def get_squares(a, b): return a*a, b*b
print(Square.get_squares(3, 5)) |
В скрипте выше мы создали класс под названием Square
со статичным методом get_squares()
. Метод принимает два параметра. Он умножает каждый параметр на себя и возвращает оба результата при помощи оператора return
. В выдаче указанного выше скрипта вы увидите квадраты 3 и 5.
Метод str
До этого момента мы выводили атрибуты при помощи метода print()
. Посмотрим, что случится, если мы выведем объект класса.
Для этого нам нужно создать простой класс Car
с одним методом и попытаться вывести объект класса в консоль. Выполним следующий скрипт:
class Car: # создание методов класса def start(self): print («Двигатель заведен») car_a = Car() print(car_a)
class Car:
# создание методов класса def start(self): print («Двигатель заведен»)
car_a = Car() print(car_a) |
В скрипте выше мы создали объект car_a
класса Car
и вывели его значение на экран. По сути мы относимся к объекту car_a
как к строке. Выдача выглядит следующим образом:
<__main__.Car object at 0x000001CCCF4335C0>
<__main__.Car object at 0x000001CCCF4335C0> |
Выдача показывает локацию памяти, где хранится наш объект. Каждый объект Python по умолчанию содержит метод __str__ .
Когда вы используете объект в качестве строки, вызывается метод __str__
, который по умолчанию выводит локацию памяти объекта. Однако, вы также можете предоставить собственное определение метода __str__
. Например, как в следующем примере:
# создание класса Car class Car: # создание методов класса def __str__(self): return «Car class Object» def start(self): print («Двигатель заведен») car_a = Car() print(car_a)
# создание класса Car class Car:
# создание методов класса def __str__(self): return «Car class Object»
def start(self): print («Двигатель заведен»)
car_a = Car() print(car_a) |
В скрипте выше, мы переопределили метод __str__
, предоставив наше собственное определение метода. Теперь, если вы выведите объект car_a, вы увидите сообщение «Car class Object» в консоли. Это сообщение, которое мы внесли в наш пользовательский метод __str__
.
Использование этого метода позволяет вам создавать пользовательские и более осмысленные описания, когда объект выводится. Вы можете даже отобразить кое-какие данные внутри класса, такие как название класса Car
.
Конструкторы
Конструктор — это специальный метод, который вызывается по умолчанию когда вы создаете объект класса.
Для создания конструктора вам нужно создать метод с ключевым словом __init__
. Взгляните на следующий пример:
class Car: # создание атрибутов класса car_count = 0 # создание методов класса def __init__(self): Car.car_count +=1 print(Car.car_count)
class Car:
# создание атрибутов класса car_count = 0
# создание методов класса def __init__(self): Car.car_count +=1 print(Car.car_count) |
В скрипте выше мы создали класс Car
с одним атрибутом класса car_count
. Класс содержит конструктор, который увеличивает значение car_count
и выводит итоговое значение на экран.
Теперь, когда объект класса Car
будет создан, конструктор также будет вызван, значение car_count
увеличится и отобразится на экране. Создадим простой объект и посмотрим, что выйдет:
car_a = Car() car_b = Car() car_c = Car()
car_a = Car() car_b = Car() car_c = Car() |
В выдаче вы увидите выведенное значение 1, 2 и 3, поскольку для каждого объекта значение переменной car_count
увеличивается и отображается на экране.
За исключением названия, конструктор может использоваться как обычный метод. Вы можете передавать и получать значения из конструктора. Он обычно используется таким образом, когда вам нужно инициализировать значения атрибута при создании экземпляра класса.
Локальные переменные против глобальных
Мы знаем, что есть два типа атрибутов Python: атрибуты экземпляра и атрибуты класса.
Атрибуты класса также называются переменными. В зависимости от области видимости, переменные также могут относиться к двум типам: локальные переменные и глобальные переменные.
Локальные переменные
Локальная переменная в классе — это переменная, доступ к которой возможен только внутри блока кода, в котором она определена. Например, если вы определите переменную внутри метода, к нему не удастся получить доступ откуда-либо вне метода. Посмотрим на следующий скрипт:
# создаем класс Car class Car: def start(self): message = «Двигатель заведен» return message
# создаем класс Car class Car: def start(self): message = «Двигатель заведен» return message |
В скрипте выше мы создали локальную переменную message
внутри метода start()
класса Car
. Теперь создадим объект класса Car
и попытаемся получить доступ к локальной переменной message
, как показано ниже:
car_a = Car() print(car_a.message)
car_a = Car() print(car_a.message) |
Скрипт выше приводит к следующей ошибке AttributeError:
AttributeError: ‘Car’ object has no attribute ‘message’
AttributeError: ‘Car’ object has no attribute ‘message’ |
Это связано с тем, что мы не можем получить доступ к локальной переменной вне блока, где эта локальная переменная была определена.
Глобальная переменная
Глобальная переменная определяется вне любого блока, то есть метода, операторов-if, и тому подобное. Доступ к глобальной переменной может быть получен где угодно в классе. Рассмотрим следующий пример.
# создаем класс Car class Car: message1 = «Двигатель заведен» def start(self): message2 = «Автомобиль заведен» return message2 car_a = Car() print(car_a.message1)
# создаем класс Car class Car: message1 = «Двигатель заведен»
def start(self): message2 = «Автомобиль заведен» return message2
car_a = Car() print(car_a.message1) |
В этом скрипте мы создали глобальную переменную message1
и вывели ее значение на экран. В выдаче вы увидите значение переменной message1
, выведенной без ошибки.
Обратите внимание на то, что существует разница между атрибутами класса и экземпляра, а также между глобальными и локальными переменными.
Атрибуты экземпляра и класса отличаются способом получения доступа к ним. Другими словами, речь идет об использовании названия класса и использовании названия экземпляра. С другой стороны, глобальные и локальные переменные отличаются своими областями видимости, другими словами, местами, где к ним может быть получен доступ.
Доступ к локальной переменной может быть получен только внутри метода. Хотя в этой статье локальные переменные и атрибуты экземпляров определяются внутри метода, локальные переменные определяются собственным ключевым словом.
Модификаторы доступа
Модификаторы доступа в Python используются для модификации области видимости переменных по умолчанию. Есть три типа модификаторов доступов в Python ООП:
- публичный — public;
- приватный — private;
- защищенный — protected.
Доступ к переменным с модификаторами публичного доступа открыт из любой точки вне класса, доступ к приватным переменным открыт только внутри класса, и в случае с защищенными переменными, доступ открыт только внутри того же пакета.
Для создания приватной переменной, вам нужно проставить префикс двойного подчеркивание __
с названием переменной.
Для создания защищенной переменной, вам нужно проставить префикс из одного нижнего подчеркивания _
с названием переменной. Для публичных переменных, вам не нужно проставлять префиксы вообще.
Давайте взглянем на публичные, приватные и защищенные переменные в действии. Выполните следующий скрипт:
class Car: def __init__(self): print («Двигатель заведен») self.name = «corolla» self.__make = «toyota» self._model = 1999
class Car: def __init__(self): print («Двигатель заведен») self.name = «corolla» self.__make = «toyota» self._model = 1999 |
Здесь мы создали простой класс Car
с конструктором и тремя переменными: name
, make
, и model
(название, марка и модель). Переменная name
является публичной, в то время как переменные make
и model
являются приватными и защищенными, соответственно.
Давайте создадим объект класса Car
и попытаемся получить доступ к переменной name
. Выполним следующий скрипт:
car_a = Car() print(car_a.name)
car_a = Car() print(car_a.name) |
Так как name
является публичной переменной, мы можем получить к ней доступ не из класса. В выдаче вы увидите значение переменной name
, выведенное в консоли.
Теперь попробуем вывести значение переменной make
. Выполняем следующий скрипт:
В выдаче мы получим следующее уведомление об ошибке:
AttributeError: ‘Car’ object has no attribute ‘make’
AttributeError: ‘Car’ object has no attribute ‘make’ |
Мы рассмотрели большую часть основных концепций объектно-ориентированного программирования в предыдущих двух секциях. Теперь, поговорим о столбах объектно-ориентированного программирования:
- Полиморфизм;
- Наследование;
- Инкапсуляция.
Наследование
Наследование в объектно-ориентированном программировании очень похоже на наследование в реальной жизни, где ребенок наследует те или иные характеристики его родителей в дополнение к его собственным характеристикам.
В объектно-ориентированном программировании, наследование означает отношение IS-A
. Например, болид — это транспорт. Наследование это одна из самых удивительных концепций объектно-ориентированного программирования, так как оно подразумевает повторное использование.
Основная идея наследования в объектно-ориентированном программировании заключается в том, что класс может наследовать характеристики другого класса. Класс, который наследует другой класс, называется дочерним классом или производным классом, и класс, который дает наследие, называется родительским, или основным.
Мы собрали ТОП Книг для Python программиста которые помогут быстро изучить язык программирования Python. Список книг: Книги по PythonРассмотрим на очень простой пример наследования. Выполним следующий скрипт:
# Создание класса Vehicle class Vehicle: def vehicle_method(self): print(«Это родительский метод из класса Vehicle») # Создание класса Car, который наследует Vehicle class Car(Vehicle): def car_method(self): print(«Это метод из дочернего класса»)
# Создание класса Vehicle class Vehicle: def vehicle_method(self): print(«Это ро |
python-scripts.com
Python. Урок 14. Классы и объекты
Данный урок посвящен объектно-ориентированному программированию в Python. Разобраны такие темы как создание объектов и классов, работа с конструктором, наследование и полиморфизм в Python.
Объектно-ориентированное программирование (ООП) является методологией разработки программного обеспечения, в основе которой лежит понятие класса и объекта, при этом сама программа создается как некоторая совокупность объектов, которые взаимодействую друг с другом и с внешним миром. Каждый объект является экземпляром некоторого класса. Классы образуют иерархии. Более подробно о понятии ООП можно прочитать на википедии.
Выделяют три основных “столпа” ООП- это инкапсуляция, наследование и полиморфизм.
Инкапсуляция
Под инкапсуляцией понимается объединение в рамках одной сущности (класса) определенных данных и методов для работы с ними (и не только). Например, можно определить класс “холодильник”, который будет содержать следующие данные: производитель, объем, количество камер хранения, потребляемая мощность и т.п., и методы: открыть/закрыть холодильник, включить/выключить. При этом класс становится новым типом данных в рамках разрабатываемой программы. Можно создавать переменные этого нового типа, такие переменные называются объекты.
Наследование
Под наследованием понимается возможность создания нового класса на базе существующего. Наследование предполагает наличие отношения “является” между классом наследником и классом родителем. При этом класс потомок будет содержать те же атрибуты и методы, что и базовый класс, но при этом его можно (и нужно) расширять через добавление новых методов и атрибутов.
Примером базового класса, демонстрирующего наследование, можно определить класс “автомобиль”, имеющий атрибуты: масса, мощность двигателя, объем топливного бака и методы: завести и заглушить. У такого класса может быть потомок – “грузовой автомобиль”, он будет содержать те же атрибуты и методы, что и класс “автомобиль”, и дополнительные свойства: количество осей, мощность компрессора и т.п..
Полиморфизм
Полиморфизм позволяет одинаково обращаться с объектами, имеющими однотипный интерфейс, независимо от внутренней реализации объекта. Например с объектом класса “грузовой автомобиль” можно производить те же операции, что и с объектом класса “автомобиль”, т.к. первый является наследником второго, при этом обратное утверждение неверно (во всяком случае не всегда). Другими словами полиморфизм предполагает разную реализацию методов с одинаковыми именами. Это очень полезно при наследовании, когда в классе наследнике можно переопределить методы класса родителя.
Создание классов и объектов
Создание класса в Python начинается с инструкции class. Вот так будет выглядеть минимальный класс.
class C: pass
Класс состоит из объявления (инструкция class), имени класса (нашем случае это имя C) и тела класса, которое содержит атрибуты и методы (в нашем минимальном классе есть только одна инструкция pass).
Для того чтобы создать объект класса необходимо воспользоваться следующим синтаксисом:
имя_объекта = имя_класса()
Класс может содержать атрибуты и методы. Ниже представлен класс, содержащий атрибуты color (цвет), width (ширина), height (высота).
class Rectangle: color = “green” width = 100 height = 100
Доступ к атрибуту класса можно получить следующим образом.
имя_объекта.атрибут
rect1 = Rectangle() print(rect1.color)
Добавим к нашему классу метод. Метод – это функция находящаяся внутри класса, выполняющая определенную работу, которая, чаще всего, предполагает доступ к атрибутам созданного объекта. Например, нашему классу Rectangle, можно добавить метод, считающий площадь прямоугольника. Для того, чтобы метод в классе знал, с каким объектом он работает (это нужно для того, чтобы получить доступ к атрибутам: ширина (width) и высота (height)), первым аргументом ему следует передать параметр self, через который он может получить доступ к своим данным.
class Rectangle: color = "green" width = 100 height = 100 def square(self): return self.width * self.height
Тогда, наша конечная программа, демонстрирующая работу с атрибутами и методами, будет выглядеть так:
class Rectangle: color = "green" width = 100 height = 100 def square(self): return self.width*self.height rect1 = Rectangle() print(rect1.color) print(rect1.square()) rect2 = Rectangle() rect2.width = 200 rect2.color = "brown" print(rect2.color) print(rect2.square())
Конструктор класса
Конструктор класса позволяет задать определенные параметры объекта при его создании. Таким образом появляется возможность создавать объекты с уже заранее заданными атрибутами. Конструктором класса является метод:
__init__(self)
Например, для того, чтобы иметь возможность задать цвет, длину и ширину прямоугольника при его создании, добавим к классу Rectangle следующий конструктор:
class Rectangle: def __init__(self, color="green", width=100, height=100): self.color = color self.width = width self.height = height def square(self): return self.width * self.height rect1 = Rectangle() print(rect1.color) print(rect1.square()) rect1 = Rectangle("yellow", 23, 34) print(rect1.color) print(rect1.square())
В организации наследования участвуют как минимум два класса: класс родитель и класс потомок. При этом возможно множественное наследование, в этом случае у класса потомка есть несколько родителей. Не все языки программирования поддерживают множественное наследование, но в Python можно его использовать.
Синтаксически создание класса с указанием его родителя/ей выглядит так:
class имя_класса(имя_родителя1, [имя_родителя2,…, имя_родителя_n])
Доработаем наш пример так, чтобы в нем присутствовало наследование.
class Figure: def __init__(self, color): self.color = color def get_color(self): return self.color class Rectangle(Figure): def __init__(self, color, width=100, height=100): super().__init__(color) self.width = width self.height = height def square(self): return self.width*self.height rect1 = Rectangle("blue") print(rect1.get_color()) print(rect1.square()) rect2 = Rectangle("red", 25, 70) print(rect2.get_color()) print(rect2.square())
Как уже было сказано во введении в рамках ООП полиморфизм, как правило, используется с позиции переопределения методов базового класса в классе наследнике. Проще всего это рассмотреть на примере. Добавим в наш базовый класс метод info(), который печатает сводную информацию по объекту класса Figure и переопределим этот метод в классе Rectangle, где добавим дополнительные данные и вывод.
class Figure: def __init__(self, color): self.color = color def get_color(self): return self.color def info(self): print("Figure") print("Color: " + self.color) class Rectangle(Figure): def __init__(self, color, width=100, height=100): super().__init__(color) self.width = width self.height = height def square(self): return self.width * self.height def info(self): print("Rectangle") print("Color: " + self.color) print("Width: " + str(self.width)) print("Height: " + str(self.height)) print("Square: " + str(self.square())) fig1 = Figure("green") print(fig1.info()) rect1 = Rectangle("red", 24, 45) print(rect1.info())
Таким образом наследник класса может расширять и модифицировать функционал класса родителя.
P.S.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
<<< Python. Урок 13. Модули и пакеты Python. Урок 15. Итераторы и генераторы>>>
devpractice.ru
Объектно-ориентированное программирование. Классы и объекты
Сегодня мы поговорим об объектно-ориентированном программировании и о его применении в python.
Объектно-ориентированное программирование (ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.
Класс — тип, описывающий устройство объектов. Объект — это экземпляр класса. Класс можно сравнить с чертежом, по которому создаются объекты.
Python соответствует принципам объектно-ориентированного программирования. В python всё является объектами — и строки, и списки, и словари, и всё остальное.
Но возможности ООП в python этим не ограничены. Программист может написать свой тип данных (класс), определить в нём свои методы.
Это не является обязательным — мы можем пользоваться только встроенными объектами. Однако ООП полезно при долгосрочной разработке программы несколькими людьми, так как упрощает понимание кода.
Приступим теперь собственно к написанию своих классов на python. Попробуем определить собственный класс:
>>> # Пример простейшего класса, который ничего не делает ... class A: ... pass
Теперь мы можем создать несколько экземпляров этого класса:
>>> a = A() >>> b = A() >>> a.arg = 1 # у экземпляра a появился атрибут arg, равный 1 >>> b.arg = 2 # а у экземпляра b - атрибут arg, равный 2 >>> print(a.arg) 1 >>> print(b.arg) 2 >>> c = A() >>> print(c.arg) # а у этого экземпляра нет arg Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'A' object has no attribute 'arg'
Классу возможно задать собственные методы:
>>> class A: ... def g(self): # self - обязательный аргумент, содержащий в себе экземпляр ... # класса, передающийся при вызове метода, ... # поэтому этот аргумент должен присутствовать ... # во всех методах класса. ... return 'hello world' ... >>> a = A() >>> a.g() 'hello world'
И напоследок еще один пример:
>>> class B: ... arg = 'Python' # Все экземпляры этого класса будут иметь атрибут arg, ... # равный "Python" ... # Но впоследствии мы его можем изменить ... def g(self): ... return self.arg ... >>> b = B() >>> b.g() 'Python' >>> B.g(b) 'Python' >>> b.arg = 'spam' >>> b.g() 'spam'
pythonworld.ru
методы и атрибуты классов ~ PythonRu
Python — объектно-ориентированный язык с начала его существования. Поэтому, создание и использование классов и объектов в Python просто и легко. Эта статья поможет разобраться на примерах в области поддержки объектно-ориентированного программирования Python. Если у вас нет опыта работы с объектно-ориентированным программированием (OOП), ознакомьтесь с вводным курсом или учебным пособием, чтобы понять основные понятия.
Создание классов
Оператор class
создает новое определение класса. Имя класса сразу следует за ключевым словом class
, после которого ставиться двоеточие:
class ClassName:
"""Необязательная строка документации класса"""
class_suite
- У класса есть строка документации, к которой можно получить доступ через
ClassName.__doc__
. class_suite
состоит из частей класса, атрибутов данных и функции.
Пример создания класса
Создание простого класса на Python
class Employee:
"""Базовый класс для всех сотрудников"""
emp_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.emp_count += 1
def display_count(self):
print('Всего сотрудников: %d' % Employee.empCount)
def display_employee(self):
print('Имя: {}. Зарплата: {}'.format(self.name, self.salary))
- Переменная
emp_count
— переменная класса, значение которой разделяется между экземплярами этого класса. Получить доступ к этой переменной можно черезEmployee.emp_count
из класса или за его пределами. - Первый метод
__init__()
— специальный метод, который называют конструктором класса или методом инициализации. Его вызывает Python при создании нового экземпляра этого класса. - Объявляйте другие методы класса, как обычные функции, за исключением того, что первый аргумент для каждого метода
self
. Python добавляет аргументself
в список для вас; и тогда вам не нужно включать его при вызове этих методов.
Создание экземпляров класса
Чтобы создать экземпляры классов, нужно вызвать класс с использованием его имени и передать аргументы, которые принимает метод __init__
.
emp1 = Employee("Андрей", 2000)
emp2 = Employee("Мария", 5000)
Доступ к атрибутам
Получите доступ к атрибутам класса, используя оператор .
после объекта класса. Доступ к классу можно получить используя имя переменой класса:
emp1.display_employee()
emp2.display_employee()
print("Всего сотрудников: %d" % Employee.emp_count)
Теперь, систематизируем все.
class Employee:
"""Базовый класс для всех сотрудников"""
emp_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.emp_count += 1
def display_count(self):
print('Всего сотрудников: %d' % Employee.emp_count)
def display_employee(self):
print('Имя: {}. Зарплата: {}'.format(self.name, self.salary))
emp1 = Employee("Андрей", 2000)
emp2 = Employee("Мария", 5000)
emp1.display_employee()
emp2.display_employee()
print("Всего сотрудников: %d" % Employee.emp_count)
При выполнении этого кода, мы получаем следующий результат:
Имя: Андрей. Зарплата: 2000
Имя: Мария. Зарплата: 5000
Всего сотрудников: 2
Вы можете добавлять, удалять или изменять атрибуты классов и объектов в любой момент.
emp1.age = 7
emp1.age = 8
del emp1.age
Вместо использования привычных операторов для доступа к атрибутам вы можете использовать эти функции:
getattr(obj, name [, default])
— для доступа к атрибуту объекта.hasattr(obj, name)
— проверить, есть ли вobj
атрибутname
.setattr(obj, name, value)
— задать атрибут. Если атрибут не существует, он будет создан.delattr(obj, name)
— удалить атрибут
hasattr(emp1, 'age')
getattr(emp1, 'age')
setattr(emp1, 'age', 8)
delattr(empl, 'age')
Встроенные атрибуты класса
Каждый класс Python хранит встроенные атрибуты, и предоставляет к ним доступ через оператор .
, как и любой другой атрибут
Тест на знание python
Какая функция удаляет объект из списка?
Продолжить
Какие операторы можно использовать со строками?
Продолжить
Какое значение вернет код colors[2] ?
Продолжить
Что выведет этот код?
Продолжить
Какой будет результат выполнения этого кода?
Продолжить
Продолжить
{{title}}
{{image}}
{{content}}
{{/global}}Поделиться результатами через
__dict__
— словарь, содержащий пространство имен класса.__doc__
— строка документации класса.None
если, документация отсутствует.__name__
— имя класса.__module__
— имя модуля, в котором определяется класс. Этот атрибут__main__
в интерактивном режиме.__bases__
— могут быть пустые tuple, содержащие базовые классы, в порядке их появления в списке базового класса.
Для вышеуказанного класса давайте попробуем получить доступ ко всем этим атрибутам:
class Employee:
"""Базовый класс для всех сотрудников"""
emp_count = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def display_count(self):
print('Всего сотрудников: %d' % Employee.empCount)
def display_employee(self):
print('Имя: {}. Зарплата: {}'.format(self.name, self.salary))
print("Employee.__doc__:", Employee.__doc__)
print("Employee.__name__:", Employee.__name__)
print("Employee.__module__:", Employee.__module__)
print("Employee.__bases__:", Employee.__bases__)
print("Employee.__dict__:", Employee.__dict__)
Когда этот код выполняется, он возвращает такой результат:
Employee.__doc__: Базовый класс для всех сотрудников
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: (<class 'object'>,)
Employee.__dict__: {'__module__': '__main__', '__doc__': 'Базовый класс для всех сотрудников', 'emp_count': 0, '__init__': <function Employee.__init__ at 0x03C7D7C8>, 'display_count': <function Employee.display_count at 0x03FA6AE0>, 'display_employee': <function Employee.display_employee at 0x03FA6B28>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
Уничтожение объектов (сбор мусора)
Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов), чтобы освободить пространство памяти. С помощью процесса ‘Garbage Collection’ Python периодически восстанавливает блоки памяти, которые больше не используются.
Сборщик мусора Python запускается во время выполнения программы и тогда, когда количество ссылок на объект достигает нуля. С изменением количества обращений к нему, меняется количество ссылок.
Когда объект присваивают новой переменной или добавляют в контейнер (список, кортеж, словарь), количество ссылок объекта увеличивается. Количество ссылок на объект уменьшается, когда он удаляется с помощью del
, или его ссылка выходит за пределы видимости. Когда количество ссылок достигает нуля, Python автоматически собирает его.
a = 40 # создали объект <40>
b = a # увеличивает количество ссылок <40>
c = [b] # увеличивает количество ссылок <40>
del a # уменьшает количество ссылок <40>
b = 100 # уменьшает количество ссылок <40>
c[0] = -1 # уменьшает количество ссылок <40>
Обычно вы не заметите, когда сборщик мусора уничтожает экземпляр и очищает свое пространство. Но классом можно реализовать специальный метод __del__()
, называемый деструктором. Он вызывается, перед уничтожением экземпляра. Этот метод может использоваться для очистки любых ресурсов памяти.
Пример работы __del__()
Деструктор __del__()
выводит имя класса того экземпляра, который должен быть уничтожен
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print('{} уничтожен'.format(class_name))
pt1 = Point()
pt2 = pt1
pt3 = pt1
print(id(pt1), id(pt2), id(pt3))
del pt1
del pt2
del pt3
Когда вышеуказанный код выполняется и выводит следующее:
17692784 17692784 17692784
Point уничтожен
В идеале вы должны создавать свои классы в отдельном модуле. Затем импортировать их в основной модуль программы с помощью
import SomeClass
.
Наследование класса
Вместо того, чтобы начинать с нуля, вы можете создать класс, на основе уже существующего. Укажите родительский класс в круглых скобках после имени нового класса.
Класс наследник наследует атрибуты своего родительского класса. Вы можете использовать эти атрибуты так, как будто они определены в классе наследнике. Он может переопределять элементы данных и методы родителя.
Синтаксис наследования класса
Классы наследники объявляются так, как и родительские классы. Только, список наследуемых классов, указан после имени класса.
class SubClassName (ParentClass1[, ParentClass2, ...]):
"""Необязательная строка документации класса"""
class_suite
Пример наследования класса в Python
class Parent:
parent_attr = 100
def __init__(self):
print('Вызов родительского конструктора')
def parent_method(self):
print('Вызов родительского метода')
def set_attr(self, attr):
Parent.parent_attr = attr
def get_attr(self):
print('Атрибут родителя: {}'.format(Parent.parent_attr))
class Child(Parent):
def __init__(self):
print('Вызов конструктора класса наследника')
def child_method(self):
print('Вызов метода класса наследника')
c = Child()
c.child_method()
c.parent_method()
c.set_attr(200)
c.get_attr()
Когда этот код выполняется, он выводит следующий результат:
Вызов конструктора класса наследника
Вызов метода класса наследника
Вызов родительского метода
Атрибут родителя: 200
Аналогичным образом вы можете управлять классом с помощью нескольких родительских классов:
class A: # объявите класс A
....
class B: # объявите класс B
....
class C(A, B): # C наследуется от A и B
....
Вы можете использовать функции issubclass()
или isinstance()
для проверки отношений двух классов и экземпляров.
- Логическая функция
issubclass(sub, sup)
возвращает значениеTrue
, если данный подклассsub
действительно является подклассомsup
. - Логическая функция
isinstance(obj, Class)
возвращаетTrue
, еслиobj
является экземпляром классаClass
или является экземпляром подкласса класса.
Переопределение методов
Вы всегда можете переопределить методы родительского класса. В вашем подклассе могут понадобиться специальные функции. Это одна из причин переопределения родительских методов.
Пример переопределения методов
class Parent:
def my_method(self):
print('Вызов родительского метода')
class Child(Parent):
def my_method(self):
print('Вызов метода наследника')
c = Child()
c.my_method()
Когда этот код выполняется, он производит следующий результат:
Вызов метода наследника
Популярные базовые методы
В данной таблице перечислены некоторые общие функции. Вы можете переопределить их в своих собственных классах.
№ | Метод, описание и пример вызова |
---|---|
1 | __init__(self [, args...]) — конструктор (с любыми необязательными аргументами)obj = className(args) |
2 | __del__(self) — деструктор, удаляет объектdel obj |
3 | __repr__(self) — оценочное строковое представлениеrepr(obj) |
4 | __str__(self) — печатное строковое представлениеstr(obj) |
Пример использования __add__
Предположим, вы создали класс Vector
для представления двумерных векторов. Что происходит, когда вы используете дополнительный оператор для их добавления? Скорее всего, Python будет против.
Однако вы можете определить метод __add__
в своем классе для добавления векторов и оператор +
будет вести себя так как нужно.
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector ({}, {})'.format(self.a, self.b)
def __add__(self, other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
При выполнении этого кода, мы получим:
Vector(7, 8)
Приватные методы и атрибуты класса
Атрибуты класса могут быть не видимыми вне определения класса. Вам нужно указать атрибуты с __
вначале, и эти атрибуты не будут вызваны вне класса.
Пример приватного атрибута
class JustCounter:
__secret_count = 0
def count(self):
self.__secret_count += 1
print(self.__secret_count)
counter = JustCounter()
counter.count()
counter.count()
print(counter.__secret_count)
При выполнении данного кода, имеем следующий результат:
Traceback (most recent call last):
File "test.py", line 12, in <module>
print(counter.__secret_count)
AttributeError: 'JustCounter' object has no attribute '__secret_count'
Вы можете получить доступ к таким атрибутам, так object._className__attrName
. Если вы замените свою последнюю строку следующим образом, то она будет работать.
.........................
print(counter._JustCounter__secretCount)
При выполнении кода, получаем результат:
1
2
2
pythonru.com
Классы в Python 3 и объекты — примеры наследования и что означает self
Классы в языке Python являются важнейшей частью объектно-ориентированного подхода к программированию. Класс описывает пользовательский тип данных, на основе которого в программе создаются однородные объекты. Как правило, они могут включать в себя некие свойства и методы, позволяющие реализовать их текущее состояние, а также поведение. В статье описаны классы для начинающих и чайников в Python 3, а так же чтобы освежить знания опытным программистам.
Объектно-ориентированное программирование
Объектно-ориентированный подход к разработке ПО был призван стать надежной заменой для структурной методологии программирования. Согласно этой уже устаревшей концепции, каждая отдельно взятая программа является иерархической структурой из функциональных блоков кода.
В свою очередь, ООП предлагает несколько иной способ реализации программ, представляя их в виде совокупности объектов, взаимодействующих между собой.
Благодаря такой особенности:
- Улучшается восприятие поставленной задачи при работе над проектом;
- Сокращается количество строк кода;
- Уменьшается сложность написания кода.
Основными принципами ООП являются следующие механизмы: абстракция, инкапсуляция, наследование и полиморфизм. Для создания программ, обрабатывающих информацию в виде объектов, необходимо понимание, а также комплексное соблюдение всех четырех парадигм. Практическое применение каждой из них можно встретить в примерах из данной статьи.
Рассмотрим основные принципы ООП:
- Абстракция выполняет основную задачу ООП, позволяя программисту формировать объекты определенного типа с различными характеристиками и поведением, благодаря применению классов.
- Инкапсуляция помогает скрыть детали реализации конкретных объектов и защитить их свойства от постороннего вмешательства.
- Наследование дает возможность расширять уже существующие классы за счет автоматического переноса их параметров и методов к новым структурам данных.
- Полиморфизм используется для создания единого интерфейса, который имеет множество различных реализаций, зависящих от класса применяемого объекта.
Создание класса и объекта
Чтобы определить новый класс в своей программе, необходимо напечатать ключевое слово class, а после него добавить имя для создаваемой структуры данных, завершив ввод вставкой двоеточия. Следующий пример демонстрирует генерацию пустого класса с именем Example. Как можно заметить, в нем полностью отсутствует какая-либо информация.
class Example: pass example = Example()
Несмотря на пустое тело класса Example, на его основе уже можно создать определенный объект, обладающий уникальным идентификатором. Последняя строка кода, находящегося выше, представляет собой пример генерации объекта с именем example и типом данных Example. Здесь используется оператор присваивания, а также пустые круглые скобки после названия класса, прямо как в вызове метода не имеющего никаких аргументов.
Определив новый класс, можно создавать сколько угодно объектов на его основе. Как уже было сказано выше, такая структура данных может включать в себя некие свойства, то есть переменные, которыми будет наделен каждый экземпляр класса. Ниже приведен простой пример класса и объекта Python 3. В примере описывается класс под названием Data со строкой word и числом number.
class Data: word = "Python" number = 3 data = Data() print(data.word + " " + str(data.number)) Python 3
Если создать объект, основанный на классе Data, то он получит обе переменные, а также их значения, которые были определены изначально. Таким образом, был сгенерирован объект data. Получить доступ к его полям с именами word и number можно с помощью оператора точки, вызвав его через экземпляр класса. Функция print поможет вывести значения полей объекта data на экран. Не стоит забывать и о том, что число следует привести к строчному виду для того чтобы обработать его в методе print вместе с текстовым значением.
Помимо полей, пользовательский класс может включать в себя и методы, которыми будут наделены все его экземпляры. Вызвать выполнение определенного метода через созданный объект можно так же, как и получить доступ к его полям, то есть с помощью точки. Данный пример демонстрирует класс Data с функцией sayHello, которая выводит текст на экран.
class Data: def sayHello(self): print("Hello World!") data = Data() data.sayHello() Hello World!
Для того чтобы вызвать метод sayHello, нужно создать объект, принадлежащий требуемому классу Data. После этого можно запустить функцию через сгенерированный экземпляр с идентификатором data, что позволит вывести небольшое текстовое сообщение.
Аргумент self
Рассмотрим зачем нужен и что означает self в функциях Python. Как можно было заметить, единственным атрибутом для метода из класса является ключевое слово self. Помещать его нужно в каждую функцию чтобы иметь возможность вызвать ее на текущем объекте. Также с помощью этого ключевого слова можно получать доступ к полям класса в описываемом методе. Self таким образом заменяет идентификатор объекта.
class Dog: name = "Charlie" noise = "Woof!" def makeNoise(self): print(self.name + " says: " + self.noise + " " + self.noise) dog = Dog() dog.makeNoise() Charlie says: Woof! Woof!
Вверху представлен класс Dog, описывающий собаку. Он обладает полями name (имя) со стартовым значением «Charlie» и noise (шум), содержащим звук, который издает животное. Метод makeNoise заставляет собаку лаять, выдавая соответствующее сообщение на экран. Для этого в функции print используется получение доступа к полям name и noise. Далее необходимо создать экземпляр класса Dog и вызвать на нем makeNoise.
Конструктор
В предыдущих примерах кода все создаваемые объекты получали значения для своих полей напрямую из класса, так как они были заданы по умолчанию. Изменить внутренние данные любого объекта можно с помощью оператора доступа к свойствам объекта. Но существует возможность заранее определить поля для объекта, задав их во время его создания. Для этой цели в ООП используется конструктор, принимающий необходимые параметры. Следующий пример показывает работу конструктора во время инициализации объекта класса Dog.
class Dog: def __init__(self, name, breed): self.name = name self.breed = breed dog = Dog("Max", "German Shepherd") print(dog.name + " is "+ dog.breed) Max is German Shepherd
Внешне конструктор похож на обычный метод, однако вызвать его явным образом нельзя. Вместо этого он автоматически срабатывает каждый раз, когда программа создает новый объект для класса, в котором он расположен. Имя у каждого конструктора задается в виде идентификатора __init__. Получаемые им параметры можно присвоить полям будущего объекта, воспользовавшись ключевым словом self, как в вышеописанном примере.
Таким образом, класс Dog содержит два поля: name (имя) и breed (порода). Конструктор принимает параметры для изменения этих свойств во время инициализации нового объекта под названием dog. Каждый класс содержит в себе по крайней мере один конструктор, если ни одного из них не было задано явно. Однако в том случае, когда программист добавляет в свой класс конструктор с некими параметрами, конструктор, не обладающий параметрами, работать не будет. Чтобы им воспользоваться, нужно явно прописать его в классе.
Ключевой особенностью ООП является абстракция, благодаря которой есть возможность создавать частные объекты на основе общего класса, то есть определенного абстрактного понятия, такого как собака, поскольку она может иметь свое имя, породу, вес, рост.
Деструктор
Работа с деструктором, как правило, является прерогативой языков, предоставляющих более широкие возможности для управления памятью. Несмотря на грамотную работу сборщика мусора, обеспечивающего своевременное удаление ненужных объектов, вызов деструктора все еще остается доступным. Переопределить его можно в классе, задав имя __del__.
class Data: def __del__(self): print "The object is destroyed" data = Data() del(data) The object is destroyed
Как и конструктор, деструктор может содержать некий пользовательский код, сообщающий об успешном завершении работы метода. В данном примере создается экземпляр класса Data и вызывается его деструктор, принимающий в качестве параметра сам объект.
Наследование
Возможность одному классу выступать в качестве наследника для другого, перенимая тем самым его свойства и методы, является важной особенностью ООП.
Благодаря этой важной особенности пропадает необходимость переписывания кода для подобных по назначению классов.
При наследовании классов в Python обязательно следует соблюдать одно условие: класс-наследник должен представлять собой более частный случай класса-родителя. В следующем примере показывается как класс Person (Человек) наследуется классом Worker (Работник). При описании подкласса в Python, имя родительского класса записывается в круглых скобках.
class Person: name = "John" class Worker(Person): wage = 2000 human = Worker() print(human.name + " earns $" + str(human.wage)) John earns $2000
Person содержит поле name (имя), которое передается классу Worker, имеющему свойство wage (заработная плата). Все условия наследования соблюдены, так как работник является человеком и также обладает именем. Теперь, создав экземпляр класса Worker под названием human, можно получить свободный доступ к полям из родительской структуры данных.
Множественное наследование
Наследовать можно не только один класс, но и несколько одновременно, обретая тем самым их свойства и методы. В данном примере класс Dog (Собака) выступает в роли подкласса для Animal (Животное) и Pet (Питомец), поскольку может являться и тем, и другим. От Animal Dog получает способность спать (метод sleep), в то время как Pet дает возможность играть с хозяином (метод play). В свою очередь, оба родительских класса унаследовали поле name от Creature (Создание). Класс Dog также получил это свойство и может его использовать.
class Creature: def __init__(self, name): self.name = name class Animal(Creature): def sleep(self): print(self.name + " is sleeping") class Pet(Creature): def play(self): print(self.name + " is playing") class Dog(Animal, Pet): def bark(self): print(self.name + " is barking") beast = Dog("Buddy") beast.sleep() beast.play() beast.bark() Buddy is sleeping Buddy is playing Buddy is barking
В вышеописанном примере создается объект класса Dog, получающий имя в конструкторе. Затем по очереди выполняются методы sleep (спать), play (играть) и bark (лаять), двое из которых были унаследованы. Способность лаять является уникальной особенностью собаки, поскольку не каждое животное или домашний питомец умеет это делать.
Абстрактные методы
Поскольку в ООП присутствует возможность наследовать поведение родительского класса, иногда возникает необходимость в специфической реализации соответствующих методов. В качестве примера можно привести следующий код, где классы Dog (Собака) и Cat (Кошка) являются потомками класса Animal (Животное). Как и положено, они оба наследуют метод makeNoise (шуметь), однако в родительском классе для него не существует реализации.
Все потому, что животное представляет собой абстрактное понятие, а значит не способно издавать какой-то конкретный звук. Однако для собаки и кошки данная команда зачастую имеет общепринятое значение. В таком случае можно утверждать, что метод makeNoise из Animal является абстрактным, поскольку не имеет собственного тела реализации.
class Animal: def __init__(self, name): self.name = name def makeNoise(self): pass class Dog(Animal): def makeNoise(self): print(self.name + " says: Woof!") class Cat(Animal): def makeNoise(self): print(self.name + " says: Meow!") dog = Dog("Baxter") cat = Cat("Oliver") dog.makeNoise() cat.makeNoise() Baxter says: Woof! Oliver says: Meow!
Как видно из примера, потомки Dog и Cat получают makeNoise, после чего переопределяют его каждый по-своему. В этом заключается суть полиморфизма, позволяющего изменять ход работы определенного метода исходя из нужд конкретного класса. При этом название у него остается общим для всех наследников, что помогает избежать путаницы с именами.
Статические методы
В предыдущих примерах все методы классов вызывались при помощи объектов, имеющих соответствующий тип. Однако пользоваться таким подходом неудобно, когда в программе нет нужды в обращении к каким-либо специфическим свойствам класса. К примеру, есть определенная структура Math, содержащая в себе методы для арифметических вычислений. Применять ее функции можно не создавая объект, если они помечены, как статические. Для того, чтобы отметить в классе метод как статический, в Python используется декоратор @staticmethod
.
class Math: @staticmethod def inc(x): return x + 1 @staticmethod def dec(x): return x - 1 print(Math.inc(10), Math.dec(10)) (11, 9)
В вышеописанном коде класс Math включает в себя два статических метода: inc, берущий число в качестве параметра и возвращающий результат, увеличенный на единицу, и dec, делающий все то же самое, только наоборот. Вызвать оба этих метода можно с помощью оператора точки, а также имени класса Math, где они были изначально реализованы.
Ограничение доступа
По умолчанию все свойства классов открыты для доступа извне, благодаря чему их можно в любой момент изменить по своему усмотрению при помощи оператора точки. Это не всегда хорошо, так как существуют некие риски потери информации либо введения неправильных данных, приводящих к сбоям в работе программы. Особенно это опасно, когда над проектом работает несколько программистов и не всегда очевидно, для чего нужно то или иное поле.
В такой ситуации помогает еще одна особенность ООП под названием инкапсуляция. Она предписывает применение приватных свойств класса, к которым отсутствует доступ за его пределами. Для управления содержимым объекта необходимо использовать специальные методы, именуемые getter (возвращает значение) и setter (устанавливает значение).
class Cat: __name = "Kitty" def get_name(self): return self.__name def set_name(self, name): self.__name = name cat = Cat() print(cat.get_name()) cat.set_name("Misty") print(cat.get_name()) Kitty Misty
Чтобы ограничить видимость полей, следует задать для них имя, начинающееся с двойного подчеркивания. В примере, продемонстрированном выше, класс Cat (Кошка) имеет закрытое свойство __name (имя), а также специальные методы get_name и set_name. Отличительной чертой такого подхода является возможность установить определенные рамки для вводимых значений. Например, можно запретить ввод отрицательного числа или пустой строки.
Свойства классов
С помощью специального механизма свойств класса можно внести корректировки в работу с оператором точки, присвоив ему собственные функции. В следующем примере представлен класс с приватным полем x, для которого написаны getter и setter. С помощью присвоения полю x специального значения функции property, получающей в качестве аргументов имена методов, можно настроить работу оператора точки согласно своим нуждам.
class Data: def __init__(self, x): self.__set_x(x) def __get_x(self): print("Get X") return self.__x def __set_x(self, x): self.__x = x print("Set X") x = property(__get_x, __set_x) data = Data(10) print(data.x) data.x = 20 print(data.x) Set X Get X 10 20
Как видно из результатов выполнения кода, методы __get_x и __set_x работают с помощью оператора точки, вызываясь самостоятельно. Таким образом, упрощается написание кода при изменении свойств объекта, а также повышается уровень безопасности данных.
Перегрузка операторов
Для обработки примитивных типов данных в языках программирования используются специальные операторы. К примеру, арифметические операции выполняются при помощи обычных знаков плюс, минус, умножить, разделить. Однако при работе с собственными типами информации вполне может потребоваться помощь этих операторов. Благодаря специальным функциям, их можно самостоятельно настроить под свои задачи.
В данном примере создается класс Point (точка), обладающий двумя полями: x и y. Для сравнения двух разных объектов такого типа можно написать специальный метод либо же просто перегрузить соответствующий оператор. Для этого потребуется переопределить функцию __eq__ в собственном классе, реализовав новое поведение в ее теле.
class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.y print(Point(2, 5) == Point(2, 5)) print(Point(3, 8) == Point(4, 6)) True False
Переопределенный метод возвращает результат сравнения двух полей у различных объектов. Благодаря этому появилась возможность сравнивать две разных точки, пользуясь всего лишь обычным оператором. Результат его работы выводится при помощи метода print.
Аналогично сравнению, можно реализовать в Python перегрузку операторов сложения, вычитания и других арифметических и логических действий. Так же можно сделать перегрузку стандартных функций str и len.
Заключение
В данной статье были рассмотрены основные особенности работы с классами в языке Python и наглядно продемонстрированы наиболее важные принципы объектно-ориентированного программирования. Работа с классами позволяет представить все данные в программе в виде взаимодействующих между собой объектов, обладающих некими свойствами и поведением.
all-python.ru
Программирование на Python: Часть 6. Классы
Сергей Яковлев
Опубликовано 02.06.2017
Серия контента:
Этот контент является частью # из серии # статей:
https://www.ibm.com/developerworks/ru/library/?series_title_by=**auto**
Следите за выходом новых статей этой серии.
Этот контент является частью серии:
Следите за выходом новых статей этой серии.
Мы переходим к одной из самых интересных тем цикла — объектно-ориентированному программированию (ООП) в Python. С точки зрения ООП, класс представляет собой коллекцию данных. Использование классов дает нам прежде всего преимущества абстрактного подхода в программировании.
- Полиморфизм: в разных объектах одна и та же операция может выполнять различные функции. Слово «полиморфизм» имеет греческую природу и означает «имеющий многие формы». Простым примером полиморфизма может служить функция
count()
, выполняющая одинаковое действие для различных типов обьектов:'abc'.count('a')
и[1, 2, 'a'].count('a')
. Оператор плюс полиморфичен при сложении чисел и при сложении строк. - Инкапсуляция: можно скрыть ненужные внутренние подробности работы объекта от окружающего мира. Это второй основной принцип абстракции. Он основан на использовании атрибутов внутри класса. Атрибуты могут иметь различные состояния в промежутках между вызовами методов класса, вследствие чего сам объект данного класса также получает различные состояния — state.
- Наследование: можно создавать специализированные классы на основе базовых. Это позволяет нам избегать написания повторного кода.
- Композиция: объект может быть составным и включать в себя другие объекты.
Объектно-ориентированный подход в программировании подразумевает следующий алгоритм действий.
- Описывается проблема с помощью обычного языка с использованием понятий, действий, прилагательных.
- На основе понятий формулируются классы.
- На основе действий проектируются методы.
- Реализуются методы и атрибуты.
Мы получили скелет — объектную модель. На основе этой модели реализуется наследование. Для проверки модели:
- пишется так называемый use cases — сценарий возможного поведения модели, где проверяется вся функциональность;
- функционал при этом может быть исправлен либо добавлен.
Объектно-ориентированный подход хорош там, где проект подразумевает долгосрочное развитие, состоит из большого количества библиотек и внутренних связей.
Механизм классов языка Python представляет собой смесь механизмов классов C++ и Modula-3. Наиболее важные особенности классов в питоне:
- множественное наследование;
- производный класс может переопределить любые методы базовых классов;
- в любом месте можно вызвать метод с тем же именем базового класса;
- все атрибуты класса в питоне по умолчанию являются public, т.е. доступны отовсюду; все методы — виртуальные, т.е. перегружают базовые.
Сегодня мы рассмотрим следующие аспекты объектно-ориентированного программирования.
- Что такое класс.
- Атрибуты класса.
- self.
- Наследование.
- ООП в действии: пример создания классов.
1. Что такое класс
Класс — это пользовательский тип. Простейшая модель определения класса выглядит следующим образом:
class имя_класса: инструкция 1 . инструкция №
Каждая такая запись генерирует свой объект класса. Отличие от C++ в том, что в плюсах описание класса — это лишь объявление, а в питоне — это создание объекта. Есть также другой тип объекта — инстанс класса, который генерируется при вызове:
инстанс_класса = имя_класса()
Объект класса и инстанс класса — это два разных объекта. Первый генерируется на этапе объявления класса, второй — при вызове имени класса. Объект класса может быть один, инстансов класса может быть сколько угодно.
Инструкция — это, как правило, определение функции. При определении класса создается новое пространство имен и создается объект-класс, который является оболочкой для всех инструкций.
Объекты классов поддерживают два вида операций:
- доступ к атрибутам;
- создание экземпляра класса.
2. Атрибуты класса
Атрибуты класса бывают двух видов:
- атрибуты данных;
- атрибуты-методы.
Атрибуты данных обычно записываются сверху. Память для атрибутов выделяется в момент их первого присваивания — либо снаружи, либо внутри метода. Методы начинаются со служебного слова def.
Доступ к атрибутам выполняется по схеме obj.attrname.
Пример.
class Simple: u'Простой класс' var = 87 def f(x): return 'Hello world'
Здесь Simple.var и Simple.f — пользовательские атрибуты. Есть также стандартные атрибуты:
>>> print Simple.__doc__
Целое число
>>> print Simple.var.__doc__ int(x[, base]) -> integer ...
Создание экземпляра класса похоже на то, как будто мы делаем вызов функций:
smpl = Simple()
Будет создан пустой объект smpl. Если мы хотим, чтобы при создании выполнялись какие-то действия, нужно определить конструктор, который будет вызываться автоматически:
class Simple: def __init__(self): self.list = []
При создании объекта smpl будет создан пустой список list. Конструктору можно передать аргументы:
class Simple: def __init__(self, count, str): self.list = [] self.count = count self.str = str >>> s = Simple(1,'22') >>> s.count, s.str 1 22
Атрибут данных можно сделать приватным (private) — т.е. недоступным снаружи — для этого слева нужно поставить два символа подчеркивания:
class Simple: u'Простой класс с приватным атрибутом' __private_attr = 10 def __init__(self, count, str): self.__private_attr = 20 print self.__private_attr s = Simple(1,'22') print s.__private_attr
Последняя строка вызовет исключение — атрибут __private_attr годен только для внутреннего использования.
Методы необязательно определять внутри тела класса:
def method_for_simple(self, x, y): return x + y class Simple: f = method_for_simple >>> s = Simple() >>> print s.f(1,2) 3
Пустой класс можно использовать в качестве заготовки для структуры данных:
class Customer: pass custom = Customer() custom.name = 'Вася' custom.salary = 100000
3. self
Обычно первый аргумент в имени метода — self. Как говорит автор языка Гвидо Ван Россум, это не более чем соглашение: имя self не имеет абсолютно никакого специального значения.
self полезен для того, чтобы обращаться к другим атрибутам класса:
class Simple: def __init__(self): self.list = [] def f1(self): self.list.append(123) def f2(self): self.f1() >>> s = Simple() >>> s.f2() >>> print s.list [123]
Self — это аналог «this» в C++.
4. Наследование
Определение производного класса выглядит следующим образом:
class Derived(Base):
Если базовый класс определен не в текущем модуле:
class Derived(module_name.Base):
Разрешение имен атрибутов работает сверху вниз: если атрибут не найден в текущем классе, поиск продолжается в базовом классе, и так далее по рекурсии. Производные классы могут переопределить методы базовых классов — все методы являются в этом смысле виртуальными. Вызвать метод базового класса можно с префиксом:
Base.method()
В питоне существует ограниченная поддержка множественного наследования:
class Derived(Base1,Base2,Base3):
Поиск атрибута производится в следующем порядке:
- в Derived;
- в Base1, затем рекурсивно в базовых классах Base1;
- в Base2, затем рекурсивно в базовых классах Base2
- и т.д.
5. Пример
Создадим два класса: Person — хранит общую информацию о людях — имя, профессия, зарплата; класс Manager — специализированный производный класс. В классе Person мы создадим свою версию для стандартной встроенной функции str, которая есть по умолчанию в любом питоновском классе — для этого она будет иметь префикс с двумя символами подчеркивания слева и справа. Когда мы попытаемся распечатать инстанс класса, будет вызвана __str__.
# -*- coding: utf-8 -*- class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) def __str__(self): return '[Person: %s, %s]' % (self.name, self.pay) class Manager(Person): def __init__(self, name, pay): Person.__init__(self, name, 'mgr', pay) def giveRaise(self, percent, bonus=100): Person.giveRaise(self, percent + bonus)
Создаем первый инстанс класса Person:
>>> ivan = Person('Иван Petrov')
Создаем второй инстанс класса Person:
>>> john = Person('John Sidorov', job='dev', pay=100000)
Вызываем перегруженную функцию __str__;
>>> print(ivan) >>> print(john)
Выводим фамилию:
>>> print(ivan.lastName(), john.lastName())
Начисляем премиальные:
>>> john.giveRaise(.10)
И получаем:
>>> print(john)
Создаем инстанс класса Manager:
>>> tom = Manager('Tom Jones', 50000)
Начисляем мегапремиальные:
>>> tom.giveRaise(.10)
Выводим:
print(tom.lastName()) print(tom)
Вывод:
[Person: Иван Petrov, 0] [Person: John Sidorov, 100000] ('Petrov', 'Sidorov') [Person: John Sidorov, 110000] Jones [Person: Tom Jones, 5055000]
Заключение
Основные свойства ООП — полиморфизм, наследование, инкапсуляция. Класс — это пользовательский тип, состоящий из методов и атрибутов. Инстанс класса создается путем вызова имени класса как функции с параметрами. Объект состоит из атрибутов и методов. Атрибут — это переменная, метод — это функция. Отличия метода от функции в том, что у него есть первый параметр — self. Полиморфизм позволяет нам работать с различными типами объектов так, что нам не нужно задумываться о том, к какому типу они принадлежат. Объекты могут скрывать (инкапсулировать) свое внутреннее состояние. Это достигается за счет того, что доступ к атрибутам осуществляется не напрямую, а через методы. Класс может быть производным от одного или нескольких классов. Производный класс наследует все методы базового класса. Базовых классов может быть несколько. Объектный дизайн должен быть прозрачным, понятным и описан, что называется, в ‘терминах человеческого языка’.
В следующей статье мы расскажем о работе со специальными методами и атрибутами классов. Код примеров проверялся на версии питона 2.6.
< Предыдущая статья. Следующая статья >
Ресурсы для скачивания
Подпишите меня на уведомления к комментариям
www.ibm.com
🔵 Классы в Python
Всё в Пайтоне является объектами. Это очень расплывчатое утверждение, если до этого вы не изучали программирование вообще. Это означает, что каждый объект в Пайтоне имеет метод и значение по той причине, что все объекты базируются на классе. Класс – это проект объекта. Давайте посмотрим на примере, что это значит:
[‘__add__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getitem__’, ‘__getnewargs__’, ‘__getslice__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__mod__’, ‘__mul__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__rmod__’, ‘__rmul__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘_formatter_field_name_split’, ‘_formatter_parser’, ‘capitalize’, ‘center’, ‘count’, ‘decode’, ‘encode’, ‘endswith’, ‘expandtabs’, ‘find’, ‘format’, ‘index’, ‘isalnum’, ‘isalpha’, ‘isdigit’, ‘islower’, ‘isspace’, ‘istitle’, ‘isupper’, ‘join’, ‘ljust’, ‘lower’, ‘lstrip’, ‘partition’, ‘replace’, ‘rfind’, ‘rindex’, ‘rjust’, ‘rpartition’, ‘rsplit’, ‘rstrip’, ‘split’, ‘splitlines’, ‘startswith’, ‘strip’, ‘swapcase’, ‘title’, ‘translate’, ‘upper’, ‘zfill’]
[‘__add__’, ‘__class__’, ‘__contains__’, ‘__delattr__’, ‘__doc__’, ‘__eq__’, ‘__format__’, ‘__ge__’, ‘__getattribute__’, ‘__getitem__’, ‘__getnewargs__’, ‘__getslice__’, ‘__gt__’, ‘__hash__’, ‘__init__’, ‘__le__’, ‘__len__’, ‘__lt__’, ‘__mod__’, ‘__mul__’, ‘__ne__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__rmod__’, ‘__rmul__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘_formatter_field_name_split’, ‘_formatter_parser’, ‘capitalize’, ‘center’, ‘count’, ‘decode’, ‘encode’, ‘endswith’, ‘expandtabs’, ‘find’, ‘format’, ‘index’, ‘isalnum’, ‘isalpha’, ‘isdigit’, ‘islower’, ‘isspace’, ‘istitle’, ‘isupper’, ‘join’, ‘ljust’, ‘lower’, ‘lstrip’, ‘partition’, ‘replace’, ‘rfind’, ‘rindex’, ‘rjust’, ‘rpartition’, ‘rsplit’, ‘rstrip’, ‘split’, ‘splitlines’, ‘startswith’, ‘strip’, ‘swapcase’, ‘title’, ‘translate’, ‘upper’, ‘zfill’] |
В примере мы видим строку, присвоенную переменной х. Это может выглядеть как большой объем, но дело в том, что у этой строки много методов. Если вы используете ключевое слово dir, вы получите список всех методов, которые можно присвоить строке. Мы видим 71 метод! Технически, мы не можем вызвать методы, которые начинаются с подчеркивание, так что это сужает список до 38 методов, но это все еще очень много! Что это значит? Это значит что, строка основана на классе, а переменная х – и есть экземпляр этого класса. В Пайтоне мы можем создавать собственные классы. Начнем!
Создание Класса
Создание класса в Пайтоне – это очень просто. Вот простой пример:
# Python 2.x syntax class Vehicle(object): «»»docstring»»» def __init__(self): «»»Constructor»»» pass
# Python 2.x syntax
class Vehicle(object): «»»docstring»»»
def __init__(self): «»»Constructor»»» pass |
Этот класс не делает ничего конкретного, тем не менее, это очень хороший инструмент для изучения. Например, чтобы создать класс, мы используем ключевое слово class, за которым следует наименование класса. В Пайтоне, конвенция указывает на то, что наименование класса должно начинаться с заглавной буквы. Далее нам нужно открыть круглые скобки, за которыми следует слово object и закрытые скобки. «object» — то, на чем основан класс, или наследуется от него. Это называется базовым классом или родительским классом. Большая часть классов в Пайтоне основаны на объекте. У классов есть особый метод, под названием __init__.
Этот метод вызывается всякий раз, когда вы создаете (или создаете экземпляр) объект на основе этого класса. Метод __init__ вызывается единожды, и не может быть вызван снова внутри программы. Другое определение метода __init__ — это конструктор, кстати, этот термин редко встречается в Пайтоне. Вы можете подумать, почему я называю это методом, а не функцией? Функция меняет свое имя на «method», когда она находится внутри класса. Обратите внимание на то, что каждый метод должен иметь как минимум один аргумент, что в случае с обычной функцией уже не вяжется. В Python 3 нам не нужно прямо указывать, что мы наследуем у объекта. Вместо этого, мы можем написать это следующим образом:
# Python 3.x syntax class Vehicle: «»»docstring»»» def __init__(self): «»»Constructor»»» pass
# Python 3.x syntax
class Vehicle: «»»docstring»»»
def __init__(self): «»»Constructor»»» pass |
Обратите внимание на то, что единственная разница в том, что круглые скобки нам больше не нужны, когда мы основываем наш класс на объекте. Давайте немного расширим наше определение класса и дадим ему некоторые атрибуты и методы.
class Vehicle(object): «»»docstring»»» def __init__(self, color, doors, tires): «»»Constructor»»» self.color = color self.doors = doors self.tires = tires def brake(self): «»» Stop the car «»» return «Braking» def drive(self): «»» Drive the car «»» return «I’m driving!»
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Vehicle(object): «»»docstring»»»
def __init__(self, color, doors, tires): «»»Constructor»»» self.color = color self.doors = doors self.tires = tires
def brake(self): «»» Stop the car «»» return «Braking»
def drive(self): «»» Drive the car «»» return «I’m driving!» |
В данном примере мы добавили три атрибута и два метода. Эти три атрибута являются:
self.color = color self.doors = doors self.tires = tires
self.color = color self.doors = doors self.tires = tires |
Атрибуты описывают автомобиль. У него есть цвет, определенное количество дверей и колес. Также у него есть два метода. Метод описывает, что делает класс. В нашем случае, автомобиль может двигаться и останавливаться. Вы могли заметить, что все методы, включая первый, имеют интересный аргумент, под названием self. Давайте рассмотрим его внимательнее.
Мы собрали ТОП Книг для Python программиста которые помогут быстро изучить язык программирования Python. Список книг: Книги по PythonЧто такое self?
Классам нужен способ, что ссылаться на самих себя. Это не из разряда нарциссичного отношения со стороны класса. Это способ сообщения между экземплярами. Слово self это способ описания любого объекта, буквально. Давайте взглянем на пример, который мне кажется наиболее полезным, когда я сталкиваюсь с чем-то новым и странным:
Добавьте этот код в конец класса, который вы написали ранее и сохраните:
class Vehicle(object): «»»docstring»»» def __init__(self, color, doors, tires): «»»Constructor»»» self.color = color self.doors = doors self.tires = tires def brake(self): «»» Stop the car «»» return «Braking» def drive(self): «»» Drive the car «»» return «I’m driving!» if __name__ == «__main__»: car = Vehicle(«blue», 5, 4) print(car.color) truck = Vehicle(«red», 3, 6) print(truck.color)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Vehicle(object): «»»docstring»»»
def __init__(self, color, doors, tires): «»»Constructor»»» self.color = color self.doors = doors self.tires = tires
def brake(self): «»» Stop the car «»» return «Braking»
def drive(self): «»» Drive the car «»» return «I’m driving!»
if __name__ == «__main__»: car = Vehicle(«blue», 5, 4) print(car.color)
truck = Vehicle(«red», 3, 6) print(truck.color) |
Условия оператора if в данном примере это стандартный способ указать Пайтону на то, что вы хотите запустить код, если он выполняется как автономный файл. Если вы импортировали свой модуль в другой скрипт, то код, расположенный ниже проверки if не заработает. В любом случае, если вы запустите этот код, вы создадите два экземпляра класса автомобиля (Vehicle): класс легкового и класс грузового. Каждый экземпляр будет иметь свои собственные атрибуты и методы. Именно по этому, когда мы выводи цвета каждого экземпляра, они и отличаются друг от друга. Причина в том, что этот класс использует аргумент self, чтобы указать самому себе, что есть что. Давайте немного изменим класс, чтобы сделать методы более уникальными:
class Vehicle(object): «»»docstring»»» def __init__(self, color, doors, tires, vtype): «»»Constructor»»» self.color = color self.doors = doors self.tires = tires self.vtype = vtype def brake(self): «»» Stop the car «»» return «%s braking» % self.vtype def drive(self): «»» Drive the car «»» return «I’m driving a %s %s!» % (self.color, self.vtype) if __name__ == «__main__»: car = Vehicle(«blue», 5, 4, «car») print(car.brake()) print(car.drive()) truck = Vehicle(«red», 3, 6, «truck») print(truck.drive()) print(truck.brake())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Vehicle(object): «»»docstring»»»
def __init__(self, color, doors, tires, vtype): «»»Constructor»»» self.color = color self.doors = doors self.tires = tires self.vtype = vtype
def brake(self): «»» Stop the car «»» return «%s braking» % self.vtype
def drive(self): «»» Drive the car «»» return «I’m driving a %s %s!» % (self.color, self.vtype)
if __name__ == «__main__»: car = Vehicle(«blue», 5, 4, «car») print(car.brake()) print(car.drive())
truck = Vehicle(«red», 3, 6, «truck») print(truck.drive()) print(truck.brake()) |
В этом примере мы передаем другой параметр, чтобы сообщить классу, какой тип транспортного средства мы создаем. После этого мы вызываем каждый метод для каждого экземпляра. Если вы запустите данный код, вы получите следующий вывод:
car braking I’m driving a blue car! I’m driving a red truck! truck braking
car braking I’m driving a blue car! I’m driving a red truck! truck braking |
Это показывает, как экземпляр отслеживает свой аргумент self. Вы также могли заметить, что мы можем переместить переменные атрибутов из метода __init__ в другие методы. Это возможно потому, что все эти атрибуты связанны с аргументом self. Если бы мы этого не сделали, переменные были бы вне области видимости в конце метода __init__ .
Подклассы
Настоящая сила классов становится очевидной, когда вопрос касается подклассов. Вы, возможно, еще не поняли это, но мы уже создали подкласс, когда создавали класс, основанный на объекте. Другими словами, «подклассифицировали» объект. Так как объект – это не очень интересная тема, предыдущие примеры не уделили должного внимания такому сильному инструменту как подкласс. Давайте подклассифицируем наш класс Vehicle и узнаем, как все это работает.
class Car(Vehicle): «»» The Car class «»» #———————————————————————- def brake(self): «»» Override brake method «»» return «The car class is breaking slowly!» if __name__ == «__main__»: car = Car(«yellow», 2, 4, «car») car.brake() ‘The car class is breaking slowly!’ car.drive() «I’m driving a yellow car!»
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Car(Vehicle): «»» The Car class «»»
#———————————————————————- def brake(self): «»» Override brake method «»» return «The car class is breaking slowly!»
if __name__ == «__main__»: car = Car(«yellow», 2, 4, «car») car.brake() ‘The car class is breaking slowly!’ car.drive() «I’m driving a yellow car!» |
В этом примере, мы подклассифицировали класс Vehicle. Вы могли заметить, что мы не использовали методы __init__ и drive. Причина в том, что когда мы хотим сделать из класса подкласс, мы уже имеем все атрибуты и методы, только если мы не переопределяем их. Таким образом, вы могли заметить, что мы переопределяем метод brake и указываем ему делать кое-что другое. Другие методы остаются такими же, какими они и были до этого. Так что, когда вы указываете автомобилю тормозить, он использует оригинальный метод, и мы узнали, что мы водим желтый автомобиль. Когда мы используем значения родительского класса по умолчанию – мы называем это наследование.
Мы собрали ТОП Книг для Python программиста которые помогут быстро изучить язык программирования Python. Список книг: Книги по PythonЭто достаточно большой раздел в объектно-ориентированном программировании. Это также простой пример полиморфизма. Полиморфические классы имеют одинаковый интерфейс (методы, атрибуты), но они не контактируют друг с другом. Касаемо полиморфизма в Пайтоне, не очень сложно выяснить, что интерфейсы являются идентичными. С этого момента мы знакомимся с понятием утиная типизация. Суть утиной типизации заключается в том, что если это ходит как утка, и крякает как утка – значит, это должна быть утка.
Крайне выгодные и дешевые покупки друзей Вконтакте Вы найдете на сервисе ДокторСмм. Здесь Вам будет предложен широкий выбор, как качества добавляемых страниц, так и скорости их поступления на профиль. В общем, на сайте сделано все, чтобы Вы с максимальным комфортом подобрали для себя недорогое и безопасное предложение.
В Пайтоне, если класс содержит методы, которые называются одинаково, то не имеет значения, если реализация этих методов отлична. В любом случае, вам пока не нужно знать все подробности использования классов в Пайтоне. Вам нужно только хорошо разбираться в терминологии, если вы захотите углубиться в вопрос глубже. Вы можете найти много хороших примеров полиморфизма в Python, которые помогут вам понять, как и зачем вы можете использовать этот концепт в собственных приложениях.
Подведем итоги
Классы не такие уж и простые, но они очень и очень полезные и эффективные. С их помощью вы можете использовать переменные в методах, которые делают повторное использование кода намного проще. Я могу порекомендовать взглянуть на исходник Пайтона, для ознакомления с потрясными примерами того, как классы определяются и используются. Теперь, зная, как создавать подклассы, вы можете переопределять параметры родительского класса так, и в тех количествах, как вам угодно. Помните: если вы полностью переопределите его, вы по факту просто создадите новый класс.
python-scripts.com