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.
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!
I guess there's no reasonable way of changing the language to prevent that from happening, say, only allowing asserts on variables? (I mean obviously because it would break python, but also because it would be an inconsistant implementation.)
In the general case, this is hard because you can easily disallow a lot of useful cases that don't cause problems.
For example, say you want to assert that a particular property of your object contains something:
assert obj.whatever != None
Whoops, unless your compiler is sophisticated enough to be able to follow the call chain and ensure that the getter doesn't cause any side effects, this is now no longer allowed. You'd have to use a temporary variable, which is unnatural and prevents the very optimization you're trying to pull off.
The reason Python devs may not know this is that almost nobody runs python with -OO -- as it's mostly a no-op with barely any in-place optimisations. At least not in CPython.
Most developers who work with executable compilers tend to know about this sort of thing already; no doubt because for some of them they've done this very thing by accident and gotten burnt by it at some point or another.
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.
When it optimizes out a statement that the spec explicitly says can be optimized out? A correct compiler is one that conforms to spec. You using a statement for something outside of its intended purpose makes the compiler incorrect?
Sorry, I'm not deeply familiar with the Python spec. Outside of C or a lisp I would expect assert to behave more like a function which discards its arguments when disabled and less like a macro which stops evaluating its arguments when disabled. This expectation would obviously be incorrect in the case of Python, which is fairly explicit as to the meaning of assert[0]:
if __debug__:
if not expression: raise AssertionError
This sounds nice in theory. In practice, the compiler cannot eliminate any log statements (writing to a log file is a side effect). Thus, the decision to not execute "FOO" as for example Java does when asserts are disabled, is the correct one.
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.
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.
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".
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!)
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:
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.
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.
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.
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.
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.
In C it's easy enough to define your own macro (I like to call it "require") which behaves similarly, but doesn't "abuse" assert. Not so in Python, though.
It is silly to make the most, convenient form of error checking subtly wrong, and then castigate programmers as lazy for using it.
well it doesn't have to be, it's just a convention -- feel free to violate it, you just have to deal w/ your usage being a bit surprising, and lectures like the original blog post. :)
Just because you happen to like a particular coding technique doesn't mean its a good one, or that the developers are obliged to support you in it. For example, my preference could be to use all one character variables, because I can remember them all just fine, and they make my typing faster. However, this is obviously not a good habit, and hurts the comprehension of any other programmers that try to read my code. Or I could be addicted to using __add__ to call functions on my objects, with a hack that turns `my_object +"user"` into calling the user function on my_object. I could find this preferable to my_object.user() because it looks better, but that in no way entitles me to complain when a library calls + on a list-like object I passed it and it errors out, because I broke the contract of __add__. Python provides `assert` for a specific purpose, basically those outlined in the blog post, and using it for other things is just plain hackish, and is in no way a good practice.
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...
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.
> 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.
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...
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!