[melbourne-pug] Data type assumptions in Python

Javier Candeira javier at candeira.com
Thu Sep 5 09:07:05 CEST 2013


Ben, I think we're saying the same things in a different accent.

Just one thing. Comparability is transitive, so I don't have to test
comparability of the new element with all existing elements. I only
have to check whether it's comparable to the first or root item in the
structure. So that test is O(1), not O(n).

And all this came from a discussion on tradeoffs. Java generics
guarantee this invariant by putting the onus on the compiler. They
also make some manners of polymorphism more difficult, or at least
much more verbose. Tradeoffs!

J

On Thu, Sep 5, 2013 at 4:48 PM, Ben Finney <ben+python at benfinney.id.au> wrote:
> Javier Candeira <javier at candeira.com> writes:
>
>> On Thu, Sep 5, 2013 at 2:37 PM, Ben Finney <ben+python at benfinney.id.au> wrote:
>> > Javier Candeira <javier at candeira.com> 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
> melbourne-pug at python.org
> https://mail.python.org/mailman/listinfo/melbourne-pug


More information about the melbourne-pug mailing list