Here is a quote from https://stackoverflow.com/users/893/greg-hewgill answer to Explain Python's slice notation.
Python is kind to the programmer if there are fewer items than you ask
for. For example, if you ask for a[:-2] and a only contains one
element, you get an empty list instead of an error. Sometimes you
would prefer the error, so you have to be aware that this may happen.
So when the error is prefered, what is the Pythonic way to proceed ? Is there a more Pythonic way to rewrite this example ?
class ParseError(Exception):
pass
def safe_slice(data, start, end):
"""0 <= start <= end is assumed"""
r = data[start:end]
if len(r) != end - start:
raise IndexError
return r
def lazy_parse(data):
"""extract (name, phone) from a data buffer.
If the buffer could not be parsed, a ParseError is raised.
"""
try:
name_length = ord(data[0])
extracted_name = safe_slice(data, 1, 1 + name_length)
phone_length = ord(data[1 + name_length])
extracted_phone = safe_slice(data, 2 + name_length, 2 + name_length + phone_length)
except IndexError:
raise ParseError()
return extracted_name, extracted_phone
if __name__ == '__main__':
print lazy_parse("\x04Jack\x0A0123456789") # OK
print lazy_parse("\x04Jack\x0A012345678") # should raise ParseError
edit: the example was simpler to write using byte strings but my real code is using lists.
Using a function like safe_slice would be faster than creating an object just to perform the slice, but if speed is not a bottleneck and you are looking for a nicer interface, you could define a class with a
__getitem__to perform checks before returning the slice.This allows you to use nice slice notation instead of having to pass both the
startandstoparguments tosafe_slice.If speed is an issue, then I suggest just testing the end points instead of doing arithmetic. Item access for Python lists is O(1). The version of
safe_slicebelow also allows you to pass 2,3 or 4 arguments. With just 2 arguments, the second will be interpreted as the stop value, (similar torange).