Javier Candeira <[email protected]> writes:
> On Thu, Sep 5, 2013 at 2:37 PM, Ben Finney <[email protected]> wrote:
> > Javier Candeira <[email protected]> writes:
> >> I have a sorted list.
> >
> > What do you mean? Is this a Python builtin list that you have
> >sorted? If not, where did this ‘sorted_list’ come from?
>
> Yes, sorry, it's my own SortedList where I have overloaded __init__()
> and append() so contents have a 'sorted order' invariant.
Okay. If you are implementing a type with its own invariants, then
checking whether those invariants are true is necessary.
That is, if your code implements the invariant “every item in this
collection is comparable with each other item”, then of course you need
something in your code that's going to fail when attempting to add an
item that would violate the invariant.
I wouldn't call it “LBYL checking” (“I'm about to try something, will it
cause an error?”) or “type checking” (“Is this object of a specific
narrow set of types?”). Both of those make for poor code, and are not
needed for the purpose you describe. You're still best advised to use
EAFP and duck typing.
So, instead of checking the type of the new item, or testing whether
there's going to be an error before doing something with it, you just go
ahead and do it. To ensure your invariant, you have no option but to
compare the new item against all existing items – you're testing
behaviour, by just doing what you need to do instead of asking
permission first.
This means that adding an item to your collection will be an O(n)
operation, but invariants like you describe have a cost and this appears
to be one.
Myself, I try to avoid promising an invariant like that :-) and just
tell the reader what the code expects its arguments to do.
> - in the naive implementation, the TypeError will arise on insertion,
> when the first functional comparison is made: if newitem >
> existingitem.
What's wrong with that, then? You can combine it with the approach you
describe below:
> […] then raises its own TypeError exception manually (allowing for a
> structure-specific error message like "TypeError: unorderable types in
> sorted list:").
Good, but better to do it as a chained exception::
self.collection.insert(item)
try:
self.collection.sort()
except TypeError as exc:
raise TypeError("unorderable types in sorted list") from exc
(I'm sure Javier already knows about chained exceptions, but I hope
other readers will benefit from learning about this Python 3 feature.)
> - in the Lars implementation, there is a comparison at the top of the
> method, that way the error is raised by Python, but at a part where
> it's not confusing because it's not down in the guts of the method, so
> it can't be any other type of functional bug.
I think it's a good idea to minimise the amount of code in a ‘try’
block, for the reason you point out: you want to narrow down exactly
what could be causing an exception to be raised.
I don't think that's an argument for making additional checks at the top
of a function.
> I don't think there's any way for an OrderedList.insert(self, item)
> method not to throw a TypeError exception in these circumstances.
Of course! I think raising a TypeError is the right thing to do, when
one tries to *use* an object in a manner incompatible with its type. But
this is not *checking* the type – it is a type keeping its promises
about how its instances will behave.
That way, no one needs to check the type, because false assumptions will
lead to an appropriate exception :-)
--
\ “With Lisp or Forth, a master programmer has unlimited power |
`\ and expressiveness. With Python, even a regular guy can reach |
_o__) for the stars.” —Raymond Hettinger |
Ben Finney
_______________________________________________
melbourne-pug mailing list
[email protected]
https://mail.python.org/mailman/listinfo/melbourne-pug