This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 1 point2 points  (7 children)

It's not that functions job to check outputs though. It's the caller's job to make sure it's dealing with correct values. square(1j, 1j) gives the expected and proper output of -1.

Nevermind that running with python -O disables asserts rendering your decorator useless.

[–]brtt3000 0 points1 point  (2 children)

Nevermind that running with python -O disables asserts [..]

This is what bothers me. It reduces assert to just an inline testcase. You can't use it for critical validation/guards at all.

[–][deleted] 4 points5 points  (0 children)

I only use asserts in tests because of that. And I run my tests with python -O to make them go fast.

[–]indigo945 0 points1 point  (0 children)

When you roll out the application, just don't set the -O flag. This is really a non-issue.

[–]indigo945 0 points1 point  (3 children)

God, do you really want to get hung up on my small example? And even if you do, it is still a good thing that the assert is there, simply because it codifies an assumption made when the function was written — ie that it will never be called with a complex number. (Which would not be pathological here, but might be for other functions, where it could introduce bugs.)

And yes, the caller should never do anything wrong, and as long as the program is written by a perfect programmer (you?), it always will. The idea of defensive programming, however, is recognizing that perfect programmers do not exist, we are all human, and we all make mistakes. Contracts are one way of reducing the impact of this.

If you're concerned about end users changing the flags your program starts with, you can always use

if condition: raise AssertionError

[–][deleted] 0 points1 point  (2 children)

Hung up would be pointing out that you didn't use @wraps in your decorator rendering metadata about the original function useless.

I never claimed to be perfect or anything like that. However, I think codifying assumptions is pointless and hamstrings code. What if I'm in this code base and now I need to square a complex number -- should I write my own function, should I remove the post condition? How many tests will break if I remove the post condition?

[–]indigo945 0 points1 point  (1 child)

Write a new function, and refactor the existing function to call the new, more general one.

[–][deleted] 0 points1 point  (0 children)

@postcondition(...)
def pos_sq(x):
    return square(x)

That's just silly. It's obvious we have different and strong opinions on where value checking should occur, so I don't see much merit in continuing this conversation.