If you really need to check the type of objects, sure, use
isinstance - that's what it was invented for.
However, if your real requirement is do something different depending on the subtype of an object, then usually it's a better idea to implement this code as a method in each subclass and simply call that method on the object of dynamic type. That way, dynamic dispatch takes care of the conditionals and you don't have to write them by hand. For instance, you can define an abstract method
Violation and then have each specific type of
Violation override it to perform a type-specific response.
It can happen that the polymorphic class is manifestly the wrong place to put that code. In that case, you can compromise and write a dynamically dispatched method that does only part of the work. For instance, it might generate a detailed description of the error and hand it to the caller for dispatch via your logging mechanism. Or it might decide which course of action is appropriate (e.g. SyntaxError: fail, LexicalError: fail, TimeoutError: retry) and signal the caller what to do.
In the extreme case the
Violation could at least have a dynamic
getType() method so that you can switch over its return value, rather than have to write out
isinstance N times.