why won't slicing lists raise IndexError?

Cameron Simpson cs at cskk.id.au
Mon Dec 4 17:02:03 EST 2017


On 04Dec2017 14:13, Jason Maldonis <jjmaldonis at gmail.com> wrote:
>And I'll be honest -- I like the implementation of the LazyList I wrote
>above. I think it's pretty logical, because it allows you to think about
>the lazy list like this:  "Treat the list like a norma list. If you run out
>of bounds, get more data, then treat the list like a normal list again."
>And I really like that clean logic.

Yes, it is very attractive. I've got a buffer class with that behaviour which 
is very useful for parsing data streams.

I think the salient difference between your LazyList's API and my buffer's API 
is that in mine, the space allocation is a distinct operation from the 
__getitem__.

My buffer class keeps an internal buffer of unconsumed data, and its 
__getitem__ indexes only that. It has two methods for "on demand": .extend(), 
which ensures that the internal buffer has at least n bytes, and .take(), which 
does a .extend and then returns the leading n bytes (trimming them from the 
internal buffer of course).

So if I need to inspect the buffer I do a .extend to ensure there's enough 
data, then one can directly use [] to look at stuff, or use .take to grab a 
known size chunk.

The .extend and .take operation accept an optional "short_ok" boolean, default 
False.  When false, .extend and .take raise an exception if there aren't enough 
data available, otherwise they can return with a short buffer containing what 
was available.

That covers your slice situation: true implies Python-like slicing and false 
(the default) acts like you (and I) usually want: an exception.

And it sidesteps Python's design decisions because it leaves the __getitem__ 
semantics unchanged. One ensures the require preconditions (enough data) by 
calling .extend _first_.

Here's some example code parsing an ISO14496 Box header record, which has 2 4 
byte values at the start. The code uses short_ok to probe for immediate 
end-of-input. But if there are data, it uses .extend and .take in default mode 
so that an _incomplete_ record raises an exception:

  def parse_box_header(bfr):
    ''' Decode a box header from the CornuCopyBuffer `bfr`. Return (box_header, new_buf, new_offset) or None at end of input.
    '''
    # return BoxHeader=None if at the end of the data
    bfr.extend(1, short_ok=True)
    if not bfr:
      return None
    # note start point
    offset0 = bfr.offset
    user_type = None
    bfr.extend(8)
    box_size, = unpack('>L', bfr.take(4))
    box_type = bfr.take(4)

In summary, I think you should give your LazyList a .extend method to establish 
your code's precondition (enough elements) and then you can proceed with your 
slicing in surety that it will behave correctly.

Cheers,
Cameron Simpson <cs at cskk.id.au> (formerly cs at zip.com.au)



More information about the Python-list mailing list