Both Rust and Go have had medium sized warts and/or boilerplate around errors, especially if you need control flow to depend on the error. In Python I've never felt that way.
Not sure I can put my finger on it, because any trivial example would be fine in either paradigm. I think it has to do with the forced/unnatural upfront decision "is this error a type or an interface" that may change later on, as a type might need to be refactored to an interface. There's probably one or two other reasons I can't think of right now.
match number_of_lines_of_a_file() with
| x -> x
[ exception File_not_found -> 0
This makes it really easy to create alternative versions of functions, using exceptions or option types: match number_of_lines_of_a_file_opt() with
| Some x -> x
| None -> raise File_not_found
match number_of_lines_of_a_file_exc() with
| x -> Some x
| exception File_not_found -> None