Объектно-ориентированное программирование (ООП) — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определённого класса, а классы образуют иерархию наследования.
Ключевые черты ООП:
Класс — универсальный, комплексный тип данных, состоящий из тематически единого набора «полей» (переменных более элементарных типов) и «методов» (функций для работы с этими полями), то есть он является моделью информационной сущности с внутренним и внешним интерфейсами для оперирования своим содержимым (значениями полей). Классы служат для объединения функционала, связанного общей идеей и смыслом, в одну сущность, у которой может быть свое внутреннее состояние, а также методы, которые позволяют модифицировать это состояние.
Объект — сущность в адресном пространстве вычислительной системы, появляющаяся при создании экземпляра класса (например, после запуска результатов компиляции и связывания исходного кода на выполнение).
Типы данных (такие как int, float и др.) в Python являются классами, структуры данных (dict, list, ...) — это также классы.
print(int) print(dict)
Для того, что узнать, принадлежит ли объект к определённому типу (т.е. классу), существует стандартная функция isinstance
.
num = 13 isinstance(num, int)
class Country: pass
print(dir(Country))
russia = Country() # Создаются два экземпляра china = Country() # Каждый является отдельным пространством имен
russia.name = "Russia" # Можно получать/записывать значения атрибутов china.name = "China"
Первым аргументом метод __init__
принимает ссылку на только что созданный экземпляр класса, далее могут идти другие аргументы. Внутри инициализатора мы можем по ссылке self
установить так называемые атрибуты экземпляра. В данном случае мы ставим атрибут экземпляра name
и присваиваем ему аргумент name
— имя страны:
class Country: def __init__(self, name): self.name = name
russia = Country("Russia")
russia.__class__
class Country: def __init__(self, name): self.name = name # магический метод __str__ переопределяет то, как будет печататься объект def __str__(self): return self.name # repr() однозначное текстовое представление (representation) объекта полезное для отладки def __repr__(self): return f"Country {self.name}" countries = [] country_names = ["Russia", "Kazakhstan", "China", "Italy"] for name in country_names: country = Country(name) countries.append(country) print(countries)
Иногда нужно создать переменную, которая будет работать в контексте класса, но не будет связана с каждым конкретным экземпляром (т.е. будет относиться непосредственно к самому классу, а не к экземпляру). В этом примере count
(счётчик стран) — это атрибут класса:
class Country: count = 0 def __init__(self, name, population=None): self.name = name self.population = population or [] Country.count += 1
russia = Country("Russia") china = Country("China") print(Country.count, russia.count, china.__dict__)
Когда счетчик ссылок на экземпляр класса достигает нуля (мы уже говорили про сборщик мусора в Python и то, что он использует счетчик ссылок), вызывается метод __del__
экземпляра. Это также магический метод, который Python нам предоставляет возможность переопределить:
class Country: def __init__(self, name): self.name = name def __del__(self): print(f"Country {self.name} has been exterminated!") byzantium = Country("Byzantium")
del byzantium
Методы — это функции, которые действуют в контексте экземпляра класса. Таким образом, они могут менять состояние экземпляра, обращаясь к атрибутам экземпляра или делать любую другую полезную работу.
class Human: def __init__(self, name, age=0): self.name = name self.age = age def _say(self, text): print(text) def say_name(self): self._say(f"Hello, I am {self.name}") def say_how_old(self): self._say(f"I am {self._age} years old") class Country: def __init__(self, name, population=None): self.name = name self.population = population or [] def add_human(self, human): print(f"Welcome to {self.name}, {human.name}!") self.population.append(human)
russia = Country("Russia") settler = Human("Gérard Depardieu", age=69) settler.say_name() russia.add_human(settler)
russia.population
class Country: def __init__(self, name, population=None): self.name = name self.population = population or [] def __add__(self, other): print(f"Countries {self.name} and {other.name} are allies now!") return Country(f"Alliance of {self.name} and {other.name}")
russia = Country("Russia") belarus = Country("Belarus") (russia + belarus).name
class Country: def __init__(self, name=None): self.name = name
class Region(Country): def __init__(self, name, status=""): ''' Вызовем инициализатор родительского класса, используя функцию super() Вызов функции super() без параметров равносилен тому, что мы указали сам класс и передали туда объект self То же самое, что Region.__init__(self) ''' super().__init__(name) self.status = status def get_info(self): return "Название: {}. Статус: {}".format(self.name, self.status) len_obl = Region("Ленинградская область", "область") print(len_obl.get_info())
Множественное наследование через классы примеси
import json class ExportJSON: def to_json(self): return json.dumps({"name": self.name, "status": self.status}) class ExReg(Region, ExportJSON): pass
ExReg("Ленинградская область", "область").to_json()
print(issubclass(int, object)) print(issubclass(Region, object)) print(issubclass(Region, Country)) print(issubclass(ExReg, Country))
Также в Python существуют приватные атрибуты. Для того чтобы создать приватный атрибут, необходимо его имя записать через два символа нижнего подчёркивания. Тогда в самом классе к нему можно обращаться так же, а вот для классов-наследников этот атрибут будет уже недо- ступен.
На самом деле не так всё просто, Python просто изменяет название и оно доступно в __dict__
Попробуйте сделать задание на классы из курса Погружение в Python:
Как правило задачи про классы носят не вычислительный характер. Обычно нужно написать классы, которые отвечают определенным интерфейсам. Насколько удобны эти интерфейсы и как сильно связаны классы между собой, определит легкость их использования в будущих программах.
Предположим есть данные о разных автомобилях и спецтехнике. Данные представлены в виде таблицы с характеристиками. Обратите внимание на то, что некоторые колонки присущи только легковым автомобилям, например, кол-во пассажирских мест. В свою очередь только у грузовых автомобилей есть длина, ширина и высота кузова.
Тип (car_type) | Марка (brand) | Кол-во пассажирских мест (passenger_seats_count) | Фото (photo_file_name) | Кузов ДxШxВ, м (body_whl) | Грузоподъемность, Тонн (carrying) | Дополнительно (extra) |
car | Nissan xTtrail | 4 | f1.jpeg | 2.5 | ||
truck | Man | f2.jpeg | 8x3x2.5 | 20 | ||
car | Mazda 6 | 4 | f3.jpeg | 2.5 | ||
spec_machine | Hitachi | f4.jpeg | 1.2 | Легкая техника для уборки снега |
Вам необходимо создать свою иерархию классов для данных, которые описаны в таблице.
У любого объекта есть обязательный атрибут car_type. Он означает тип объекта и может принимать одно из значений: car, truck, spec_machine.
Также у любого объекта из иерархии есть фото в виде имени файла — обязательный атрибут photo_file_name.
В базовом классе нужно реализовать метод get_photo_file_ext для получения расширения файла (“.png”, “.jpeg” и т.д.) с фото. Расширение файла можно получить при помощи os.path.splitext.
Для грузового автомобиля необходимо разделить характеристики кузова на отдельные составляющие body_length, body_width, body_height. Разделитель — латинская буква x. Характеристики кузова могут быть заданы в виде пустой строки, в таком случае все составляющие равны 0. Обратите внимание на то, что характеристики кузова должны быть вещественными числами.
Также для класса грузового автомобиля необходимо реализовать метод get_body_volume, возвращающий объем кузова в метрах кубических.
Все обязательные атрибуты для объектов Car, Truck и SpecMachine перечислены в таблице ниже, где 1 - означает, что атрибут обязателен для объекта, 0 - атрибут должен отсутствовать.
Car | Truck | SpecMachine | |
car_type | 1 | 1 | 1 |
photo_file_name | 1 | 1 | 1 |
brand | 1 | 1 | 1 |
carrying | 1 | 1 | 1 |
passenger_seats_count | 1 | 0 | 0 |
body_width | 0 | 1 | 0 |
body_height | 0 | 1 | 0 |
body_length | 0 | 1 | 0 |
extra | 0 | 0 | 1 |
Далее необходимо реализовать функцию, на вход которой подается имя файла в формате csv. Файл содержит данные аналогичные строкам из таблицы. Вам необходимо прочитать этот файл построчно при помощи модуля стандартной библиотеки csv. Затем проанализировать строки и создать список нужных объектов с автомобилями и специальной техникой. Функция должна возвращать список объектов.
Не важно как вы назовете свои классы, главное чтобы их атрибуты имели имена:
И методы:
У каждого объекта из иерархии должен быть свой набор атрибутов и методов. У класса легковой автомобиль не должно быть метода get_body_volume в отличие от класса грузового автомобиля.
Функция, которая парсит строки входного массива, должна называться get_car_list.
Также обратите внимание, что все значения в csv файле при чтении будут python-строками. Нужно преобразовать строку в int для passenger_seats_count, во float для carrying, а также во float для body_width body_height, body_length.
Также ваша программа должна быть готова к тому, что в некоторых строках данные могут быть заполнены некорректно. Например, число колонок меньше . В таком случае нужно проигнорировать подобные строки и не создавать объекты. Строки с пустым значением для body_whl игнорироваться не должны. Вы можете использовать механизм исключений для обработки ошибок.
Ниже приведен пример с заготовкой кода для выполнения задания.
class CarBase: def __init__(self, brand, photo_file_name, carrying): pass class Car(CarBase): def __init__(self, brand, photo_file_name, carrying, passenger_seats_count): pass class Truck(CarBase): def __init__(self, brand, photo_file_name, carrying, body_whl): pass class SpecMachine(CarBase): def __init__(self, brand, photo_file_name, carrying, extra): pass def get_car_list(csv_filename): car_list = [] return car_list
Вам необходимо расширить функционал исходных классов, дополнить методы нужным кодом и реализовать функцию get_car_list
.
Комментарии