
When to use assert - martinvol
https://mail.python.org/pipermail/python-list/2013-November/660401.html
======
mickeyp
The most important thing to remember with assertions vis-a-vis optimising
compilers removing assertion calls: you should never have assertions that
cause side effects.

Bad:

assert InitialiseStuff() != False, 'Initialise failed'

If the optimising compiler is set to eliminiate assertions the InitialiseStuff
function won't get called! This will (subtly, or not so subtly) break your
program!

~~~
anonymoushn
This sounds like an incorrect compiler.

~~~
reitzensteinm
I disagree, because side effects in an assert would always be a code smell,
but placing slow code in there is useful behavior. The line could have easily
been:

assert EnsureUnique( obj )

Which runs through the program's data structures to ensure that nothing else
matches a property of obj in some way.

Hacky, slow, but very useful to keep around if you have a constraint like
that. But if you run it in release mode with production sized data sets, it'll
slow to a crawl if you don't cut out the entire check.

~~~
gbog
I wouldn't rely on assert code for this, better say "if dataset is huge don't
check".

------
mrmaddog
Once you do decide to use an assert in python, there are a couple things to
consider. The first is to make sure you are expressive in your message. Your
assert may very well hit one day, and it may only occur at the most
unpredictable times, so you'll want to make sure you have enough information
to see _why_ the assert failed. To use the OP's example:

    
    
      assert x > 0, "x is not zero or negative"
    

When this fails, you're going to be at a loss to understand what actually
happened. If you change the message to something like the following, you're
going to have a much easier time tracing through your program to understand
why your constraint was violated:

    
    
      assert x > 0, "Expected positive x, got: %d" % x
    

Once you get in the habbit of this, you'll quickly run up against style-guide
imposed line limits. My usual trick here is to use parenthesis and let
python's automatic string concatenation work its wonders, but you have to be
careful because

    
    
       assert (x > 0, "This is my longer message "
               "for x")
    

evaluates to a tuple, and thus is always true. Instead only use parens around
the message:

    
    
       assert x > 0, ("This is my longer message "
                      "for x")
    

Lastly, don't use asserts in tests. Use the standard unittest library which
will do a much better job explaining what was received, what was expected, and
what the difference between them is.

------
ggchappell
Well, I have to agree with the general point here, but it's buried inside a
mass of wandering prose, and I'm not even sure the author quite gets it.

Here is (IMHO) the point: assert is for checking invariants.

That said, I doubt that a post like this is a good place to teach people what
invariants are. Those who don't know should go learn. To some small extent,
that might include the author, as, for example, pre- and post-conditions are
not some special contract-thing that is separate from invariants; they are
special kinds of invariants. (So, yes, use assert to check contracts; that's
part of checking invariants)

And:

> You wouldn't write code like this:
    
    
      if not isinstance(x, int):
          raise AssertionError("not an int")
    

Sure I would, if x being an instance of int is an invariant of my code. But if
it isn't, then I wouldn't.

I also accept that, as the author says, "assert" has its quick-and-dirty uses.
Putting an "assert False" around an unwritten portion of code is a reasonable
thing to do, particularly if it is code that one expects to be used often, as
the "assert" will continually complain of its own existence as long as it is
there.

~~~
lockes5hadow
I agree with what you say, but just an aside: I prefer to raise
NotImplementedError for unwritten portions of code personally.

------
shin_lao
Assertions are also a great way to say: "this should be true, if this is not
true, the code behavior is undefined."

Carefully using assertions is a great way to see if you are building your code
wrong. Like a way to tell you "you're trying to fit the wrong lego piece in".

------
ak217
I was frustrated with the limited utility and expressiveness of Python
assertions, so I wrote a library for expressive assertions:

[https://github.com/kislyuk/ensure](https://github.com/kislyuk/ensure)

It's inspired by ensure.js, and defines a large (and growing) number of
helpers to make assertions concise, easy to read, customizable, and more
usable than the assert statement. (Feedback welcome!)

------
ForHackernews
Seems like this was written by Steven D'Aprano, not Guido.

~~~
pudquick
Agreed, title should be adjusted.

~~~
ivoflipse
He just tweeted it, which is why its showing up. But yes, the attribution is
wrong.

------
deckiedan
You can do type-class tricks with it if you want...

    
    
        class Username(unicode): pass
        class Directory(unicode): pass
    
        def updateUserHomeDir(name, dirname):
            assert isinstance(name, Username)
            assert isinstance(dirname, Directory)
    
            update_etc_passwd(name=name, dirname=dirname)
    

or whatever. This only really starts to become useful when you start adding
sanity checks into the classes:

    
    
        class Directory(unicode):
            def __init__(*vargs, **kwargs):
                super(Directory, self).__init__(*vargs, **kwargs)
                if not os.path.is_dir(self):
                    if not os.path.exists(self):
                        make_dirs(self)
    
    

just a bit cleverer in reality. You then move all the directory-path sanity
checks into there. You can also subclass further for `class
ValidHomeDir(Directory)`, etc.

The benefit of this is that you don't have to run your sanity checks more than
once, you can pass the values around as much as you need and be sure that they
have been initiated correctly. Use of your functions becomes _at worst_ :

    
    
        updateUserHomeDir(Username('dan'), Directory('/home/daniel'))
    

which isn't so bad, really.

And it's fine to be optimised out, as the code paths are what are being
checked here, not the user data. :-)

I don't know how "pythonic" the idea is, but it does seem reasonably elegant
to me, and solves some of the problems of python's otherwise fun duck-typing.

------
euphemize
This is really useful. I've been coding python projects for a few years now
and was never quite sure how/when to use them. You'd run into assert when unit
testing, but not that much elsewhere. This makes it clearer, I like this quote
: _Don 't use assert for checking anything that you expect might fail in the
ordinary use of your program. Assertions are for extraordinary failure
conditions._

------
penguindev
"Assert is not a shortcut for lazy coders."

Then they need to make something better for me. My c and python code isn't
functional with asserts compiled out, because assert just makes my life
easier.

In a perfect world, current assert semantics would be _DEBUG_ASSERT (all caps
to let you know it has macro-ish behavior), and the normal assert would always
be on.

~~~
natrius
Can you give an example of the Python you're writing that requires assertions
to be functional? My guess is that you're using assertions where raising and
catching exceptions would be more appropriate.

~~~
penguindev
In c, checking memory allocation. In python, checking that sql queries
succeeded. The point is there's always something that should _never_ fail in
real life but I feel better with an explicit check on it. I figure the ease in
coding makes up for telling people (not that it's a high probability of this
happening) to not compile asserts out.

It just bugs me to no end how people try to make the easiest forms of error
checking difficult.

------
gcb1
his example of design by contract is silly out of functional testing. why
would i even contact an external service/api if my own code can know what it
will get back every time? other than a side effect service, such as calling a
disk IO and checking that return code means success, but then my check does
not check anything...

------
CraigJPerry
I look at asserts as twitter for comments.

~~~
GhotiFish
I... don't follow.

~~~
meowface
I think he means that it can turn comments into "tweets" (140-characters or
fewer).

So a comment like "# y should always be a positive integer at this point, due
to checks made in the callee" would be converted into "assert y >= 0".

------
jheriko
this is a fascinating piece of advice - there is no argument against heavy use
of assertions i have heard which isn't "oh but i'm lazy and don't want to do
my job properly" and whilst that is e.g. the justification for python as a
language in the first place that we don't want to spend time writing bytes,
its not about convenience in this case but necessity. writing everything at
the lowest level is not necessary, but writing stable code is. assertions help
you do this, they will not become obsolete until e.g. static code analysis
becomes good enough to replace it.

this comment is also scary from the C family of languages background:

"When using assert properly, this is a feature, but when assert is used
inappropriately, it leads to code that is completely broken when running with
the -O flag."

why don't people test their software by merely running it? esp in the config
it will ship in.

~~~
mickeyp
> this comment is also scary from the C family of languages background:

> "When using assert properly, this is a feature, but when assert is > used
> inappropriately, it leads to code that is completely broken when running
> with the -O flag."

That's more or less how all compiled languages work - it's not a bug, nor is
it scary. Developers want things their users don't, like assertion calls,
debug symbols and additional type/bounds checking and whathave you; things
that, while helpful to a developer, would only slow down the program by
requiring greater amounts of CPU and ram to run. That's why these things are
compiled out of "release" builds.

Yes, this can introduce very rare and subtle bugs, and not just from people
making assertion calls with side effects -- but also from additional belt-and-
braces checks placed by the compiler in the dev build.

~~~
jheriko
i think you misunderstand me, what is scary is that the comment needs making
at all.

"why don't people test their software by merely running it? esp in the config
it will ship in."

i.e. why aren't you testing what you ship. i.e. with the -O flag on. the fact
that build flags cause headaches like this is well understood from my history
with languages using the C preprocessor - all C, C++ etc. programmers will
have a good handle on this from necessity i think. it scares me to think that
people are writing code but not understanding what it is going to do...

------
IgorPartola
Wouldn't the example with the if/elif be the same as the above TypeError one?
I suppose a ValueError would actually be more appropriate.

Proper design by contract support would be great too.

