Проверки на невалидность в коде

Hmmgg

Новичок
Пользователь
Дек 10, 2020
5
0
1
Имеется такой код, ошибка в тестировании при проверки на валидность: AssertionError: Тест 21.3. Вызов функции get_car_list на файле, содержащем только невалидные данные, должен возвращать пустой список. Первый элемент списка, возвращенного функцией get_car_list, имеет атрибуты: car_type: 'car', brand: '', carrying: 2.5, photo_file_name: 'f5.jpeg', passenger_seats_count: 4. assert [<solution.Ca...9487c18>, ...] == [] Left contains 18 more items, first extra item: <solution.Car object at 0x7f1e194874a8> Use -v to get the full diff
Сам код
Python:
import csv
import sys
import os.path


class CarBase:
    """Базовый класс с общими методами и атрибутами"""

    # индексы полей, которые соответствуют колонкам в исходном csv-файле
    csv_car_type = 0
    csv_brand = 1
    csv_passenger_seats_count = 2
    csv_photo_file_name = 3
    csv_body_whl = 4
    csv_carrying = 5
    csv_extra = 6

    def __init__(self, brand, photo_file_name, carrying):
        self.brand = brand
        self.photo_file_name = photo_file_name
        self.carrying = float(carrying)

    def get_photo_file_ext(self):
        _, ext = os.path.splitext(self.photo_file_name)
        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)

    @classmethod
    def instance(cls, row):
        return cls(
            row[cls.csv_brand],
            row[cls.csv_photo_file_name],
            row[cls.csv_carrying],
            row[cls.csv_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

    @classmethod
    def instance(cls, row):
        return cls(
            row[cls.csv_brand],
            row[cls.csv_photo_file_name],
            row[cls.csv_carrying],
            row[cls.csv_body_whl],
        )


class SpecMachine(CarBase):
    """Класс спецтехника"""

    car_type = 'spec_machine'

    def __init__(self, brand, photo_file_name, carrying, extra):
        super().__init__(brand, photo_file_name, carrying)
        self.extra = extra

    @classmethod
    def instance(cls, row):
        return cls(
            row[cls.csv_brand],
            row[cls.csv_photo_file_name],
            row[cls.csv_carrying],
            row[cls.csv_extra],
        )


def get_car_list(csv_filename):
    with open(csv_filename) as csv_fd:
        # создаем объект csv.reader для чтения csv-файла
        reader = csv.reader(csv_fd, delimiter=';')

        # пропускаем заголовок csv
        next(reader)

        # это наш список, который будем возвращать
        car_list = []

        # объявим словарь, ключи которого - тип автомобиля (car_type),
        # а значения - класс, объект которого будем создавать
        create_strategy = {
            car_class.car_type: car_class for car_class in (Car, Truck, SpecMachine)
        }

        # обрабатываем csv-файл построчно
        for row in reader:
            try:
                # определяем тип автомобиля
                car_type = row[CarBase.csv_car_type]
            except IndexError:
                # если не хватает колонок в csv - игнорируем строку
                continue

            try:
                # получаем класс, объект которого нужно создать
                # и добавить в итоговый список car_list
                car_class = create_strategy[car_type]
            except KeyError:
                # если car_type не извесен, просто игнорируем csv-строку
                continue

            try:
                # создаем и добавляем объект в car_list
                car_list.append(car_class.instance(row))
            except (ValueError, IndexError):
                # если данные некорректны, то игнорируем их
                pass

    return car_list


if __name__ == '__main__':
    print(get_car_list(sys.argv[1]))
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Первый элемент списка, возвращенного функцией get_car_list, имеет атрибуты: car_type: 'car', brand: '', carrying: 2.5, photo_file_name: 'f5.jpeg', passenger_seats_count: 4. assert [<solution.Ca...9487c18>, ...] == [] Left contains 18 more items, first extra item: <solution.Car object at 0x7f1e194874a8> Use -v to get the full diff
Так происходит потому, что в классах Car, Truck и SpecMachine нет валидации данных.
Например проверки на отсутствие значения атрибута (марки машины и др.).
Такую проверку можно сделать например так:
Python:
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)

    @classmethod
    def instance(cls, row):
        # проверяем что все атрибуты класса не None
        if all(i for i in [row[cls.csv_brand],
                           row[cls.csv_photo_file_name],
                           row[cls.csv_carrying],
                           row[cls.csv_passenger_seats_count]]
                if i is not None):
            return cls(
                row[cls.csv_brand],
                row[cls.csv_photo_file_name],
                row[cls.csv_carrying],
                row[cls.csv_passenger_seats_count],
            )
        else:
            raise ValueError
 
  • Мне нравится
Реакции: Hmmgg

Hmmgg

Новичок
Пользователь
Дек 10, 2020
5
0
1
Так происходит потому, что в классах Car, Truck и SpecMachine нет валидации данных.
Например проверки на отсутствие значения атрибута (марки машины и др.).
Такую проверку можно сделать например так:
Python:
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)

    @classmethod
    def instance(cls, row):
        # проверяем что все атрибуты класса не None
        if all(i for i in [row[cls.csv_brand],
                           row[cls.csv_photo_file_name],
                           row[cls.csv_carrying],
                           row[cls.csv_passenger_seats_count]]
                if i is not None):
            return cls(
                row[cls.csv_brand],
                row[cls.csv_photo_file_name],
                row[cls.csv_carrying],
                row[cls.csv_passenger_seats_count],
            )
        else:
            raise ValueError
Я изменил код под условия задачи и у меня теперь ошибка когда тестируется файл csv
Код с оформлением (BB-коды):
car_type;brand;passenger_seats_count;photo_file_name;body_whl;carrying;extra
car;Nissan xTtrail;4;f1.jpeg;;2.5;
truck;Man;;f2.png;8x3x2.5;20;
truck;Man;;f2.png;;20;
car;Mazda 6;4;f3.jpeg;;2.5;
;;;
spec_machine;Hitachi;;f4;;1.2;Легкая техника для уборки снега
получается такая ошибка: Total tests: 166. Tests failed: 6, Errors: 0. Total time: 0.396. Failed test - test_21_1. E AssertionError: Тест 21.1.3. Вызов функции get_car_list на файле с данными coursera_week3_cars.csv (из описания задания), вернул список с неверным количеством объектов: 0, ожидалось: 4. assert 0 == 4 + where 0 = len([])
 

Hmmgg

Новичок
Пользователь
Дек 10, 2020
5
0
1
Я изменил код под условия задачи и у меня теперь ошибка когда тестируется файл csv
Код с оформлением (BB-коды):
car_type;brand;passenger_seats_count;photo_file_name;body_whl;carrying;extra
car;Nissan xTtrail;4;f1.jpeg;;2.5;
truck;Man;;f2.png;8x3x2.5;20;
truck;Man;;f2.png;;20;
car;Mazda 6;4;f3.jpeg;;2.5;
;;;
spec_machine;Hitachi;;f4;;1.2;Легкая техника для уборки снега
получается такая ошибка: Total tests: 166. Tests failed: 6, Errors: 0. Total time: 0.396. Failed test - test_21_1. E AssertionError: Тест 21.1.3. Вызов функции get_car_list на файле с данными coursera_week3_cars.csv (из описания задания), вернул список с неверным количеством объектов: 0, ожидалось: 4. assert 0 == 4 + where 0 = len([])
уже не актуально, исправил эту часть. Вопрос как реализовать проверку - photo_file_name должно быть непустой строкой в формате "file-path.ext", где ext должно быть одно из ['.jpg', '.jpeg', '.png', '.gif'] Выходит ошибка:
Total tests: 166. Tests failed: 116, Errors: 0. Total time: 0.387. Failed test - test_8[Nissan-t1.jpg-2.5-2.4x2.3x2]. E ValueError: could not convert string to float: 't1.jpg' During handling of the above exception, another exception occurred: E AssertionError: Тест 8. Создание экземпляра класса Truck с параметрами ('Nissan', 't1.jpg', '2.5', '2.4x2.3x2') завершается выбросом исключения ValueError. assert False
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Вопрос как реализовать проверку - photo_file_name должно быть непустой строкой в формате "file-path.ext", где ext должно быть одно из ['.jpg', '.jpeg', '.png', '.gif']
У вас в классе CarBase есть метод get_photo_file_ext.
Можно добавить проверку в него, а в конструкторе вызывать этот метод чтобы проверка срабатывала.
Также можно производить валидацию общих параметров для классов в базовом классе CarBase, а в производных классах проверять только параметры, относящиеся к конкретному классу (Car, Truck, SpecMachine).
Вот пример класса CarBase с проверками:
Python:
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
 

Hmmgg

Новичок
Пользователь
Дек 10, 2020
5
0
1
У вас в классе CarBase есть метод get_photo_file_ext.
Можно добавить проверку в него, а в конструкторе вызывать этот метод чтобы проверка срабатывала.
Также можно производить валидацию общих параметров для классов в базовом классе CarBase, а в производных классах проверять только параметры, относящиеся к конкретному классу (Car, Truck, SpecMachine).
Вот пример класса CarBase с проверками:
Python:
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
Я совсем запутался, даже если вставить проверку, ошибка на 8 тесте, итоговый код такой:

Python:
import csv
import sys
import os.path


class CarBase:
    """Базовый класс с общими методами и атрибутами"""

    
    csv_car_type = 0
    csv_brand = 1
    csv_passenger_seats_count = 2
    csv_photo_file_name = 3
    csv_body_whl = 4
    csv_carrying = 5
    csv_extra = 6

    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, carrying, photo_file_name, passenger_seats_count):
        super().__init__(brand, carrying, photo_file_name)
        self.passenger_seats_count = int(passenger_seats_count)

    @classmethod
    def instance(cls, row):
        if all(i for i in [row[cls.csv_brand],
                           row[cls.csv_carrying],
                           row[cls.csv_photo_file_name],
                           row[cls.csv_passenger_seats_count]]
                if i is not None):
            return cls(
                row[cls.csv_brand],
                row[cls.csv_carrying],
                row[cls.csv_photo_file_name],
                row[cls.csv_passenger_seats_count],
            )
        else:
            raise ValueError


class Truck(CarBase):
    """Класс грузовой автомобиль"""

    car_type = 'truck'

    def __init__(self, brand, carrying, photo_file_name, body_whl):
        super().__init__(brand, photo_file_name, carrying)
        
        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

    @classmethod
    def instance(cls, row):
        if all(i for i in [row[cls.csv_brand],
                           row[cls.csv_carrying],
                           row[cls.csv_photo_file_name],
                           row[cls.csv_body_whl]]
                if i is not None):
            return cls(
                row[cls.csv_brand],
                row[cls.csv_carrying],
                row[cls.csv_photo_file_name],
                row[cls.csv_body_whl],
            )
        else:
            raise ValueError


class SpecMachine(CarBase):
    """Класс спецтехника"""

    car_type = 'spec_machine'

    def __init__(self, brand, carrying,photo_file_name, extra):
        super().__init__(brand, photo_file_name, carrying)
        self.extra = extra

    @classmethod
    def instance(cls, row):
        if all(i for i in [row[cls.csv_brand],
                           row[cls.csv_carrying],
                           row[cls.csv_photo_file_name],
                           row[cls.csv_extra]]
                if i is not None):
            return cls(
                row[cls.csv_brand],
                row[cls.csv_carrying],
                row[cls.csv_photo_file_name],
                row[cls.csv_extra],
            )
        else:
            raise ValueError


def get_car_list(csv_filename):
    with open(csv_filename) as csv_fd:
        reader = csv.reader(csv_fd, delimiter=';')
        next(reader)
        car_list = []
        create_strategy = {
            car_class.car_type: car_class for car_class in (Car, Truck, SpecMachine)
        }

      
        for row in reader:
            try:
              
                car_type = row[CarBase.csv_car_type]
            except IndexError:
                
                continue

            try:
                
                car_class = create_strategy[car_type]
            except KeyError:
                
                continue

            try:
                
                car_list.append(car_class.instance(row))
            except (ValueError, IndexError):
                
                pass

    return car_list


if __name__ == '__main__':
    print(get_car_list(sys.argv[1]))

ошибка: Total tests: 166. Tests failed: 115, Errors: 0. Total time: 0.399. Failed test - test_8[Nissan-t1.jpg-2.5-2.4x2.3x2]. E ValueError: could not convert string to float: 't1.jpg' During handling of the above exception, another exception occurred: E AssertionError: Тест 8. Создание экземпляра класса Truck с параметрами ('Nissan', 't1.jpg', '2.5', '2.4x2.3x2') завершается выбросом исключения ValueError. assert False
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Я совсем запутался, даже если вставить проверку, ошибка на 8 тесте, итоговый код такой:
Проверки в классах вызывают исключение ValueError, которое в вашем коде в функции get_car_list не отлавливается.
Вот немного переделанный код:
Python:
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]))
 
  • Мне нравится
Реакции: Hmmgg

Hmmgg

Новичок
Пользователь
Дек 10, 2020
5
0
1
Проверки в классах вызывают исключение ValueError, которое в вашем коде в функции get_car_list не отлавливается.
Вот немного переделанный код:
Python:
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]))
спасибо огромное, столько возился, все не мог разобраться нормально
 

Форум IT Специалистов