Вообще в питоне принята динамическая типизация - иными словами, если класс имеет нужные свойства и методы, то неважно какой это класс на самом деле.
Но если позарез нужно, то есть способы.
1. Динамическая проверка класса при выполнении
isinstance(x, list) проверит, что объект является списком или наследуется от списка.
isinstance(x, (list, tuple)) сделает то же для пары возможных классов
Я бы не рекомендовал такой подход, так как проверка выполняется каждый раз при выполнении, даже если её результат заведомо известен. Кроме того, нередки ситуации, когда метод обозначен как "ожидает список", но при этом всё что делает со списком - перебирает элементы. Т.е. на деле любая коллекция сработала бы.
2. Статическая проверка (погугли про python type hints)
Переписываешь заголовок метода так:
from typing import Optional
class Math:
def __init__(self, first: Int, second: Optional[Int] = None):
Если потом передать в конструктор неверные типы, сам интерпретатор питона проигнорирует это и выполнит скрипт как обычно. Но есть отдельная утилита mypy, которая статически (без запуска скрипта) анализирует скрипт и проверяет, что ты передаёшь в тот или иной метод, и выдаст ошибку если тип неверный. Таким образом можно статически убедиться в корректности скрипта, не теряя производительность во время выполнения.
3. Протоколы
Это развитие предыдущего подхода, подобие интерфейсов в других ООП языках. Они также используются совместно с mypy, интерпретатор питона их игнорирует.