Live data from Hacker News

Python exceptions considered an anti-pattern

sobolevn.me

51–60 of 69 posts

Re: Python exceptions considered an anti-pattern

#51

It amazes me how enduring formulaic it is to single out some particular design tradeoff of a language, draw up some examples of expressing something where that tradeoff creates worse code, and then act like it's some mortal flaw in the language. Python chose untyped exceptions, period. How is this surprising, given that its basis is untyped parameters? If you don't like that, use Java with its checked exceptions. Or…

Well or solve the perceived problem and use the solution (their "returns" library)...

Re: Python exceptions considered an anti-pattern

#52
> So, the sad conclusion is: all problems must be resolved individually depending on a specific usage context.

...and that's a good thing.

Recognising that specific usage contexts require specific recovery strategies is a key part of effective program design.

Division by zero is an exception, yes. That's basic math.

Getting to the point where one of your inputs is "bad" (zero in this case) shows that you have a problem. You should either catch zero up front with a conditional or catch it when it blows up, with an exception. Python favors the latter.

But without context you don't know what that bad input means and so you cannot "fix" division by zero in the general case because it isn't the division by zero that is the cause of the problem.

Asking "what should a division by zero actually return?" is the wrong question asked at the wrong point with the wrong information. Does the zero indicate lack of initialization? Does it indicate an empty container or volume? Does it indicate absence? How much of a problem to the logic of the program is this particular zero? How much of a problem is it for the person running it?

So while I personally dislike exceptions and prefer return codes, exceptions are just the messenger here and they are an effective messenger. Don't shoot them.

Re: Python exceptions considered an anti-pattern

#53

It amazes me how enduring formulaic it is to single out some particular design tradeoff of a language, draw up some examples of expressing something where that tradeoff creates worse code, and then act like it's some mortal flaw in the language. Python chose untyped exceptions, period. How is this surprising, given that its basis is untyped parameters? If you don't like that, use Java with its checked exceptions. Or…

I see a lot of commenters here are as perplexed as I was when I first read this post.

The key thing to understand here is that post is really about an attempt to implement the "Railway Oriented Programming" paradigm [1] by Scott Wlaschin, but in Python. I would suggest reading the Scott's post on ROP and at least skimming the video before going on.

So ROP is, as Scott himself states, a way to take all those nice Haskell concepts and techniques and apply them in F# in a way that won't overwhelm those who are new to them.

The problem with the original post is that it presents two problems with exception in Python and then offers the "returns" library as a solution which, ultimately, doesn't end up solving either of those problems.

The first problem the post describes is that the exceptions are not part of the function signature. The second problem is that exceptions are essentially gotos and that this makes reasoning about the execution flow very difficulty.

To tackle both problems, the returns library offers its own implementation of the Either monad in form of the Result container. Having presented that solution, the post promptly decides that monadic code in Python is unreadable and offers the @pipeline decorator which allows you to write code that looks imperative, which partially defeats its purpose as a solution to the problem about the flow reasoning. I say partially, because it replaces implicit gotos with implicit returns, which is a marginal improvement over exceptions.

The post then decides that using the Result class in the signature is also ugly and offers the @safe decorator which allows you to write the same code you would write without ROP, but now it wraps everything in Result behind the scenes. Even worse than that, this produces the return type of Result[whatever_your_success_type_is, Exception]. For those familiar with Java, this is very similar to putting "throws Exception" in your method signature, except that it's hidden and implicit.

I'll end this with a bit of advice for the author, preceded by an apology, because I'm not sure if I have managed to find a way of phrasing it that doesn't sound harsh. You might want to do some serious reading about functional programming and monads and how these concepts have been carried over into and grafted onto mixed-paradigm languages like Java. I mention Java specifically, because one of the complaints in your post is that Python won't be supporting checked exceptions in the nearest future. A lot has been written about checked exceptions in Java -- on both sides of the discussion.

[1]: https://fsharpforfunandprofit.com/rop/ [2]: https://en.wikipedia.org/wiki/Tagged_union

Re: Python exceptions considered an anti-pattern

#54

Earlier quoted context omitted.

Do you mean unchecked exceptions? Python exceptions are strictly typed, that's core to how they work. An except clause will catch subclasses of the target, so you need to have a custom type for each exception you want to raise in your code. The mistake I see people make is to use built-in exceptions without subclassing, making it impossible to explicitly catch specific errors. Or the opposite, catching Exception, whi…

No. I mean checked exceptions. At the language level, Java checked exceptions are typed, and unchecked exceptions are untyped. The Python runtime is dynamically typed . The Python language is untyped .

Both checked and unchecked exceptions are typed in Java. Unchecked exceptions are not included in the method signature, but that doesn't mean that the exceptions are untyped.

Re: Python exceptions considered an anti-pattern

#55
post #37

Here is the greatest inconvenience of Python exceptions for me. Say you have to try 10 different methods, and you only need one to work. Then you have to write 10 try...except blocks so that each next block is indented relative to previous. This creates unreadable code and does not scale for say 100 methods. The solution that came to mind is labeling try blocks and referring them in except blocks. For example: try as…

I can't think of a single time I've needed a large collection of "backup" methods in case of a chain of failures; I can't even think of an example which could scale to 100. Anyways, this solution does scale:

    methods = [method1, method2, method3, method4]
    for method in methods:
        try:
            method()
        except Exception:
            pass
        else:
            break
    else:
        raise NoMethodWorked()
You can even pair specific exceptions to each method:

    methods = [(method1, TypeError), (method2, KeyError)]
    for m, e in methods:
       try: m()
       except e: ...
But the whole thing really sounds like you're trying to do too much with one function and you really should rethink the whole structure of your code.

Re: Python exceptions considered an anti-pattern

#56
post #30

I never understood, that people who don't understand Python at all, or don't like essential Python constructs, why they bother using it? Why not use a different language in the first place? If you are using this library, you are not writing Python anymore and you lost the biggest advantage of the language: simplicity.

Often the language is chosen because it is the only option like JS for web, or it is the best option for the project. For example, you are doing physics and you need a general purpose language that is easy to write and handles gigantic numbers correctly so you pick Python.

>Often the language is chosen because it is the only option like JS for web, or it is the best option for the project. For example, you are doing physics and you need a general purpose language that is easy to write and handles gigantic numbers correctly so you pick Python.

You could pick Julia, or R. A community could create their own tools. It's not like JS, which has an absolute monopoly and people were forced to use it.

Python __became__ popular for that purpose.

Not Java. Not Haskell.

So don't try to implement Java or Haskell error handling in Python if you are using Python in a niche where it shines: it shines here because people working in that field decided it fits well.

Re: Python exceptions considered an anti-pattern

#57
post #37

Here is the greatest inconvenience of Python exceptions for me. Say you have to try 10 different methods, and you only need one to work. Then you have to write 10 try...except blocks so that each next block is indented relative to previous. This creates unreadable code and does not scale for say 100 methods. The solution that came to mind is labeling try blocks and referring them in except blocks. For example: try as…

There are several solutions that occured to me off the top of my head, here's the simplest one:

    try:
        method1()
        return
    except Exception: # or a more specific exception type
        pass # try the next method
    try:
        method2()
        return
    except Exception:
        pass
    # ...
    try:
        method10()
    except Exception:
        raise NoMethodWorked()
Seriously, there are so many ways to solve this in a readable, maintainable way, without resorting to introducing weird new syntax.

Re: Python exceptions considered an anti-pattern

#58

Earlier quoted context omitted.

No. I mean checked exceptions. At the language level, Java checked exceptions are typed, and unchecked exceptions are untyped. The Python runtime is dynamically typed . The Python language is untyped .

Both checked and unchecked exceptions are typed in Java. Unchecked exceptions are not included in the method signature, but that doesn't mean that the exceptions are untyped.

The exceptions, meaning the runtime objects themselves, are indeed typed.

But at the language level, unchecked exceptions are not part of method signatures (as you said), therefore not type- checked/declared/inferred. Therefore appropriately described as untyped -

When calling a method, you have no (formal) list of unchecked exceptions that might be raised.

If you think of exceptions as being an implicit union type around every function return (ie monad), analogous to how you have to explicitly check for errors in C/Rust, you'll see what I mean. Java's unchecked exceptions are akin to calling a function in Python that you expect to return objects of only one type, but not being "sure" that it can't return something else.

Re: Python exceptions considered an anti-pattern

#59
post #55
post #37

Here is the greatest inconvenience of Python exceptions for me. Say you have to try 10 different methods, and you only need one to work. Then you have to write 10 try...except blocks so that each next block is indented relative to previous. This creates unreadable code and does not scale for say 100 methods. The solution that came to mind is labeling try blocks and referring them in except blocks. For example: try as…

I can't think of a single time I've needed a large collection of "backup" methods in case of a chain of failures; I can't even think of an example which could scale to 100. Anyways, this solution does scale: methods = [method1, method2, method3, method4] for method in methods: try: method() except Exception: pass else: break else: raise NoMethodWorked() You can even pair specific exceptions to each method: methods =…

This is really helpful. Thank you.
Post reply on HN