Antoon Pardon wrote: > On 2006-08-28, Scott David Daniels <[EMAIL PROTECTED]> wrote: > > Antoon Pardon wrote: > >> On 2006-08-25, Simon Forman <[EMAIL PROTECTED]> wrote: > >>> ... > >>> Generally asserts should be used to "enforce" invariants of your code > >>> (as opposed to typechecking), or to check certain things while > >>> debugging. > >> > >> I don't understand this argument. Can't type checking be seen as > >> enforcing a code invariant? > >> > > But it is practically never the "right" invariant. > > And who decides what is and what is not the "right" invariant?
Um, programmers' common sense. I can't really describe it well in words, so I'll give you an example. A long time ago, I wrote a simple interpreter that broke incoming commands up on whitespace and ran the pieces one by one. Then, for reasons that are inconsequential to this post, I decided that instead of using str.split(), I wanted to keep an index into the command string and "parse" it using that. It had been a long time since I had had to do this sort of array/pointer style processing (thank you python, and Guido, et. al., too..) and I wasn't fully sure of myself and my code, so I used a bunch of assert statement to allow myself to *be* sure. Here's the relevant code (I added a few line continuations to try to prevent mail/news formatting problems, but of course YMMV): def interpret(self, command): """ Given a command string, break it into whitespace-delimited commands and execute them one after another. Integers and floats are pushed onto the stack. This variant of the Interpreter class uses a simple pointer into the command line string. It increments the 'pointer' and checks against the set of chars in string.whitespace to break up the command line. It keeps track of the command line and the index so that Words it executes (that have a reference to it) can perform manipulations on them. This permits, for example, the "chomp" word, which takes the next non-whitespace sequence of chars immediately following it on the command line, makes a string of it, and puts it on the stack. """ #Let's have a pointer or index. i = 0 #Remember our command line self.command = command #Cache the length. l = len(command) while 0 <= i < l: #Run through chars until we reach the end, #or some non-whitespace. while (i < l) and (command[i] in self.blankspace): i += 1 #If we've reached the end we're done. if i == l: break assert i < l, "If not, the line above should have break'd us."\ "We should not have i > l ever." #Remember our starting index for this word. begin = i assert command[begin] not in self.blankspace, "Just making sure." #Force at least one increment of i. i += 1 #Run through until the end or some self.blankspace. while (i < l) and (command[i] not in self.blankspace): i += 1 #At this point, we're either at the end of the command line #or we're at a blank char delimiting a preceding word. assert (i == l) or \ ((begin < i < l) and (command[i] in self.blankspace)) #We've found a word. word = command[begin:i] #first, try making an integer.. try: n = int(word) self.stack.append(n) except ValueError: #if that didn't work, try making a float.. try: f = float(word) self.stack.append(f) except ValueError: #not a float or integer, eh? Let's try executing it.. try: #Update our pointer in case the word wants it. self.i = i #try to lookup the command and execute it.. self.execute(word) #Update our pointer in case the word changed it. i = self.i except: ilog.exception('Error executing "%s"', word) #propagate the Exception "upward" raise It's not the greatest code I've ever written, in fact it's kind of naive. (Nowadays I'd probably use re.finditer()...) But notice that the assert statements all act to verify that certain conditions are met at certain places throughout the loop, and that if the code is operating correctly, i.e. in the way that the programmer (Hi there!) intended, that the assertions will always succeed. *That's* the "invariant" part. Notice further that none of the asserts have any side effects, and especially, that they could all be removed without changing the operation of the code in any way. This is the "proper" use of assertions, as I understand it. (They are of course useful when you're trying to track down odd bugs, but for a similar reason: You put them into your code at those places where you suspect your assumptions about it's workings are incorrect.) One important point I failed to bring up in my response to the OP is that assertions *can* go away. (Running python with the '-O' or '-OO' switches removes assert statements at the bytecode level, along with code blocks within the "if __debug__:" "magic" if statement.) You should never write code that will work differently or fail if it is suddenly deprived of it's assert statements one day. > > You don't usually > > mean type(x) == int, but rather something like x is a prime between 2 > > and 923. Or 5< x**4 < 429, or _something_ problem specific. saying > > that x must be an int is almost always simultaneously too specific and > > too general. > > There seem to be enough problems that work with ints but not with > floats. In such a case enforcing that the number you work with > is indeed an int seems fully appropiate. If you have a reason to restrict your code to using only ints (perhaps you're packing them into an array of some sort, or passing them to a C extension module) then yes, of course it's appropriate. However, these reasons seem to me to be much narrower (more narrow?) and more specialized (specializeder?) than they seem to me to be to you. And even in these cases, checking type and refusing to work with anything but actual ints may still be too much. Perhaps a simple call to int() might suffice. Peace, ~Simon -- http://mail.python.org/mailman/listinfo/python-list