Как правило задачи про классы не носят вычислительный характер. Обычно нужно написать классы, которые отвечают определенным интерфейсам. Насколько удобны эти интерфейсы и как сильно связаны классы между собой, определит легкость их использования в будущих программах.
Предположим есть данные о разных автомобилях и спецтехнике. Данные представлены в виде таблицы с характеристиками. Вся техника разделена на три вида: спецтехника, легковые и грузовые автомобили. Обратите внимание на то, что некоторые характеристики присущи только определенному виду техники. Например, у легковых автомобилей есть характеристика «кол-во пассажирских мест», а у грузовых автомобилей — габариты кузова: «длина», «ширина» и «высота».
Тип (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 | Легкая техника для уборки снега |
Вам необходимо создать свою иерархию классов для данных, которые описаны в таблице. Классы должны называться CarBase (базовый класс для всех типов машин), Car (легковые автомобили), Truck (грузовые автомобили) и SpecMachine (спецтехника). Все объекты имеют обязательные атрибуты:
— car_type, значение типа объекта и может принимать одно из значений: «car», «truck», «spec_machine».
— photo_file_name, имя файла с изображением машины, допустимы названия файлов изображений с расширением из списка: «.jpg», «.jpeg», «.png», «.gif»
— brand, марка производителя машины
— carrying, грузоподъемность
В базовом классе CarBase нужно реализовать метод get_photo_file_ext для получения расширения файла изображения. Расширение файла можно получить при помощи os.path.splitext.
Для грузового автомобиля необходимо в конструкторе класса определить атрибуты: body_length, body_width, body_height, отвечающие соответственно за габариты кузова — длину, ширину и высоту. Габариты передаются в параметре body_whl (строка, в которой размеры разделены латинской буквой «x»). Обратите внимание на то, что характеристики кузова должны быть вещественными числами и характеристики кузова могут быть не валидными (например, пустая строка). В таком случае всем атрибутам, отвечающим за габариты кузова, присваивается значение равное нулю.
Также для класса грузового автомобиля необходимо реализовать метод get_body_volume, возвращающий объем кузова.
В классе Car должен быть определен атрибут passenger_seats_count (количество пассажирских мест), а в классе SpecMachine — extra (дополнительное описание машины).
Полная информация о атрибутах классов приведена в таблице ниже, где 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 |
Обратите внимание, что у каждого объекта из иерархии должен быть свой набор атрибутов и методов. Например, у класса легковой автомобиль не должно быть метода get_body_volume в отличие от класса грузового автомобиля. Имена атрибутов и методов должны совпадать с теми, что описаны выше.
Далее вам необходимо реализовать функцию get_car_list, на вход которой подается имя файла в формате csv. Файл содержит данные, аналогичные строкам из таблицы. Вам необходимо прочитать этот файл построчно при помощи модуля стандартной библиотеки csv. Затем проанализировать строки на валидность и создать список объектов с автомобилями и специальной техникой. Функция должна возвращать список объектов.
Вы можете использовать для отладки работы функции get_car_list следующий csv-файл:
Первая строка в исходном файле — это заголовок csv, который содержит имена колонок. Нужно пропустить первую строку из исходного файла. Обратите внимание на то, что в некоторых строках исходного файла , данные могут быть заполнены некорректно, например, отсутствовать обязательные поля или иметь не валидное значение. В таком случае нужно проигнорировать подобные строки и не создавать объекты. Строки с пустым или не валидным значением для body_whl игнорироваться не должны. Вы можете использовать стандартный механизм обработки исключений в процессе чтения, валидации и создания объектов из строк csv-файла. Проверьте работу вашего кода с входным файлом, прежде чем загружать задание для оценки.
Пример кода, демонстрирующего чтение csv файла:
import csv import sys import os.path class CarBase: """Базовый класс с общими методами и атрибутами""" def __init__(self, brand, photo_file_name, carrying): # проверка что аргументы не являются пустой строкой if not all(i != '' for i in (brand, photo_file_name, carrying)): raise ValueError self.brand = brand self.photo_file_name = photo_file_name self.carrying = float(carrying) # вызов метода для проверки расширения файла изображения self.ext = self.get_photo_file_ext() def get_photo_file_ext(self): _, ext = os.path.splitext(self.photo_file_name) if ext not in ['.jpg', '.jpeg', '.png', '.gif']: raise ValueError return ext class Car(CarBase): """Класс легковой автомобиль""" car_type = 'car' def __init__(self, brand, photo_file_name, carrying, passenger_seats_count): super().__init__(brand, photo_file_name, carrying) self.passenger_seats_count = int(passenger_seats_count) class Truck(CarBase): """Класс грузовой автомобиль""" car_type = 'truck' def __init__(self, brand, photo_file_name, carrying, body_whl): super().__init__(brand, photo_file_name, carrying) # обрабатываем поле body_whl try: length, width, height = (float(c) for c in body_whl.split('x', 2)) except ValueError: length, width, height = .0, .0, .0 self.body_length = length self.body_width = width self.body_height = height def get_body_volume(self): return self.body_width * self.body_height * self.body_length class SpecMachine(CarBase): """Класс спецтехника""" car_type = 'spec_machine' def __init__(self, brand, photo_file_name, carrying, extra): super().__init__(brand, photo_file_name, carrying) # проверка что аргумент extra не является пустой строкой if extra == '': raise ValueError self.extra = extra def get_car_list(csv_filename): with open(csv_filename, encoding='utf-8') as csv_fd: # создаем объект csv.reader для чтения csv-файла reader = csv.reader(csv_fd, delimiter=';') # пропускаем заголовок csv next(reader) # это наш список, который будем возвращать car_list = [] # объявим словарь, ключи которого - тип автомобиля (car_type), # а значения - функция, создающая экземпляр нужного класса car_types = { 'car': lambda x: Car(x[1], x[3], x[5], x[2]), 'truck': lambda x: Truck(x[1], x[3], x[5], x[4]), 'spec_machine': lambda x: SpecMachine(x[1], x[3], x[5], x[6])} # обрабатываем csv-файл построчно for row in reader: try: car_type = row[0] # если тип машины в словаре - создаем экземпляр класса if car_type in car_types: car_list.append(car_types[car_type](row)) # при возникновении ошибки - пропускаем строку except (ValueError, IndexError): pass return car_list if __name__ == '__main__': print(get_car_list(sys.argv[1]))