<jupyter_start><jupyter_text>Slicing Slices can actually be defined using the `slice()` function which creates a `slice` object:<jupyter_code>s = slice(0, 2)
type(s)
s.start
s.stop
l = [1, 2, 3, 4, 5]
l[s]<jupyter_output><empty_output><jupyter_text>This can be useful in practice to make code more readable.Suppose you are parsing fixed-width file. You would need to define the start/end column of each field in the rows of the file.So you might write something like this:<jupyter_code>data = []  # a collection of rows, read from a file maybe
for row in data:
    first_name = row[0:51]
    last_name = row[51:101]
    ssn = row[101:111]
    # etc<jupyter_output><empty_output><jupyter_text>Instead, you might write:<jupyter_code>range_first_name = slice(0, 51)
range_last_name = slice(51, 101)
range_ssn = slice(101, 111)<jupyter_output><empty_output><jupyter_text>These might even be defined in your global scope, or maybe a config file.Then in your code you would write this instead:<jupyter_code>for row in data:
    first_name = row[range_first_name]
    last_name = row[range_last_name]
    ssn = row[range_ssn]<jupyter_output><empty_output><jupyter_text>Separating the slice definition from the code that uses the slice makes it now much easier to update your slice definitions in one place, rather than hunt for them all over the place.  Slice Fundamentals Indexing is zero-based in Python, and slices are inclusive of their start-index, and exclusive of their end-index:<jupyter_code>l = 'python'
l[0:1], l[0:6]<jupyter_output><empty_output><jupyter_text>Additionally, extended slicing allows specifying a step value:<jupyter_code>l = 'python'
l[0:6:2], l[0:6:3]<jupyter_output><empty_output><jupyter_text>And extended slices can also be defined using `slice`:<jupyter_code>s1 = slice(0, 6, 2)
s2 = slice(0, 6, 3)
l[s1], l[s2]<jupyter_output><empty_output><jupyter_text>Unlike regular indexing (e.g. `l[n]`), it's OK for slice indexes to be "out of bounds":<jupyter_code>l = [1, 2, 3, 4, 5, 6]
l[0:100]
l[-10:100]<jupyter_output><empty_output><jupyter_text>But regular indexing will raise exceptions for out of bound errors:<jupyter_code>l = [1, 2, 3, 4, 5, 6]
l[100]<jupyter_output><empty_output><jupyter_text>In slicing, if we do not specify the start/end index, Python will automatically use the start/end of the sequence we are slicing:<jupyter_code>l = [1, 2, 3, 4, 5, 6]
l[:4]
l[4:]<jupyter_output><empty_output><jupyter_text>In fact, we can omit both:<jupyter_code>l[:]<jupyter_output><empty_output><jupyter_text>In addition to the start/stop values allowing for negative values, the step value can also be negative. This simply means the sequence will traversed in the opposite direction:<jupyter_code>l = [0, 1, 2, 3, 4, 5]
l[3:0:-1]<jupyter_output><empty_output><jupyter_text>Basically we started at `3` (inclusive) and went in steps of `-1`, ending at (but not including) `0`. If we wanted to include the `0` index element, we could do it by ommitting the end value:<jupyter_code>l[3::-1]<jupyter_output><empty_output><jupyter_text>We could also do the following:<jupyter_code>l[3:-100:-1]<jupyter_output><empty_output><jupyter_text>But this would not work as expected:<jupyter_code>l[3:-1:-1]<jupyter_output><empty_output><jupyter_text>Why? Remember from the lecture that this range equivalence would be:`3 --> 3``-1  max(-1, 6-1) --> max(-1, 5) --> 5`so equivalent range would be given by:<jupyter_code>list(range(3, 5, -1))<jupyter_output><empty_output><jupyter_text>which of course is an empty range!  Easily Converting a Slice to a Range We can easily determine the effective range of a slice by using the `indices` method in the `slice` object. The only thing is that in order to do this we must know the length of the sequence we are slicing. For example, if our list has a length of 10:<jupyter_code>slice(1, 5).indices(10)
list(range(1, 5, 1))
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l[1:5]<jupyter_output><empty_output><jupyter_text>The `slice` object can also handle extended slicing:<jupyter_code>slice(0, 100, 2).indices(10)
list(range(0, 10, 2))
l[0:100:2]<jupyter_output><empty_output><jupyter_text>We can easily retrieve a list of indices from a slice by passing the unpacked tuple returned by the `indices` method to the range function's arguments and converting to a list:<jupyter_code>list(range(*slice(None, None, -1).indices(10)))<jupyter_output><empty_output><jupyter_text>As we can see from this example, using a slice such as `[::-1]` returns a sequence that is in reverse order from the original one.<jupyter_code>l
l[::]
l[::-1]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11478260869565217, 'fraction_numerical': 0.035652173913043476, 'mean_word_length': 6.1890625, 'pattern_counts': {'":': 1, '<': 81, '<?xml version=': 0, '>': 84, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '20', 'dir': 'jupyter-structured-clean-dedup', 'id': '114274', 'license': 'Apache-2.0', 'n_tokens_mistral': 1732, 'n_tokens_neox': 1619, 'n_words': 534, 'path': 'python-tuts/1-intermediate/01 - Sequences/05 - Slicing.ipynb', 'repo_name': 'timgates42/Artificial-Intelligence-Deep-Learning-Machine-Learning-Tutorials', 'size': '4600'}