
This article is a continuation of my article "
Python: collections, part 1: classification, general approaches and methods, conversion ".
In this article, we will continue to learn the general principles of working with standard collections (the collections module is not covered in it) Python.
For whom: for Python learners who already have an initial understanding of the collections and work with them, who want to systematize and deepen their knowledge, put them into a coherent picture.
')
TABLE OF CONTENTS:
- Indexing
- Slices
- Sorting
1. Indexing
1.1 Indexed Collections
Consider indexed collections (they are also called sequences - sequences) - a list (list), a tuple (tuple), a string (string).
Under indexing is meant that the elements of the collection are arranged in a certain order, each element has its own index from 0 (that is, the first element has an index not 1 but 0) up to an index one less than the length of the collection (ie len (mycollection )-one).
1.2 Getting the value by index
For all indexed collections, you can get the value of an element by its index in square brackets. Moreover, you can set a negative index, which means that we will find the element from the end, counting the reverse order.
When specifying a negative index, the last element has index -1, the penultimate -2 and so on until the first element whose index is equal to the length of the collection with a negative sign, that is (-len (mycollection).
items | a | b | c | d | e |
---|
indexes | 0 (-5) | 14) | 2 (-3) | 3 (-2) | 4 (-1) |
---|
my_str = "abcde" print(my_str[0])
Our collections can have several levels of nesting, like the list of lists in the example below. To go deeper, a second pair of square brackets is placed, and so on.
my_2lvl_list = [[1, 2, 3], ['a', 'b', 'c']] print(my_2lvl_list[0])
1.3 Changing the list item by index
Since the tuples and strings are immutable collections, we can only take elements by index, but not change them:
my_tuple = (1, 2, 3, 4, 5) print(my_tuple[0])
But for the list, if taking an element by index is located on the left side of the expression, and then comes the assignment operator =, then we set the new value to the element with this index.
my_list = [1, 2, 3, [4, 5]] my_list[0] = 10 my_list[-1][0] = 40 print(my_list)
UPD: Note: For such an assignment, the item must already exist in the list, you cannot thus add an item to a nonexistent index.
my_list = [1, 2, 3, 4, 5] my_list[5] = 6
2 slices
2.1 Slice Syntax
Very often, you need to get more than one element, and some of their set is limited to certain simple rules - for example, the first 5 or last three, or every second element - in such tasks, instead of iterating through the loop, it is much more convenient to use the so-called slice (slice slicing).
It should be remembered that by taking an element by index or slice, we do not change the original collection, we simply copied a part of it for further use (for example, adding it to another collection, printing, some calculations). Since the collection itself does not change - this applies to both mutable (list) and immutable (string, tuple) sequences.
The slice syntax is similar to that for indexing, but in square brackets instead of a single value, 2-3 is indicated with a colon:
my_collection[start:stop:step]
Features of the cut:
- Negative start and stop values ​​mean that it is necessary to count not from the beginning, but from the end of the collection.
- Negative step value - we iterate in the reverse order from right to left.
- If the start is not specified [: stop: step] - we start from the very edge of the collection, that is, from the first element (including it), if the step is positive or from the last element (including it), if the step is negative (and therefore the search goes from end to beginning ).
- If stop is not specified [start :: step] - go to the very edge of the collection, that is, to the last element (including it), if the step is positive or to the first element (including it), if the step is negative (and therefore the search goes from the end to the beginning).
- step = 1, that is, a sequential search from left to right is not necessary to specify - this is the default step value. In this case, it suffices to specify [start: stop]
- You can even do this [:] - this means taking the entire collection
- IMPORTANT : When cut, the first index is included in the sample, and the second is not!
That is, from the start, inclusive, to the stop, where the stop is not included in the result. Mathematically, this could be written as [start, stop) or explained by the following rule:
[ < > : < > : <> ]
Therefore, for example, mylist [:: - 1] is not identical to mylist [: 0: -1], since in the first case we will include all elements, and in the second we will go to 0 index, but we will not include it!
Examples of slices in the form of a table:
Code of examples from the table col = 'abcdefg' print(col[:])
2.2. Named slices
To get rid of the "magic constants", especially in the case when the same slice must be used repeatedly, you can set constants with named slices using the special function
slice () ()
Note: Non does not correspond to the omitted default value. That is, [: 2] becomes slice (None, 2), and [1 :: 2] becomes slice (1, None, 2).
person = ('Alex', 'Smith', "May", 10, 1980) NAME, BIRTHDAY = slice(None, 2), slice(2, None)
my_list = [1, 2, 3, 4, 5, 6, 7] EVEN = slice(1, None, 2) print(my_list[EVEN])
2.3 Changing the list with a slice
The important point on which attention is not always focused is that with the help of a slice you can not only get a copy of the collection, but in the case of a list you can also change the values ​​of the elements, delete and add new ones.
We illustrate this with examples below:
- Even if we want to add one element, we need to pass an iterated object, otherwise there will be a TypeError error: can only assign an iterable
my_list = [1, 2, 3, 4, 5]
- You can use a slice to insert single elements, the code for the examples is below, but I do not recommend it, since this syntax is worse to read. It is better to use the list methods .append () and .insert ():
Slice analogues .append () and insert () my_list = [1, 2, 3, 4, 5] my_list[5:] = [6]
- You can change parts of the sequence - this application looks the most interesting, since it solves the problem simply and clearly.
my_list = [1, 2, 3, 4, 5] my_list[1:3] = [20, 30] print(my_list)
- You can simply delete part of the sequence.
my_list = [1, 2, 3, 4, 5] my_list[:2] = []
2.4 Going beyond the index
Addressing by index is essentially a special case of a slice, when we refer to only one element, and not a range. But there is a very important difference in handling the situation with the missing element with the desired index.
Accessing a non-existent collection index causes an error:
my_list = [1, 2, 3, 4, 5] print(my_list[-10])
And if the cut boundaries extend beyond the boundaries of the collection, no error occurs:
my_list = [1, 2, 3, 4, 5] print(my_list[0:10])
Note : For those cases when the slice functional is not enough and more complex samples are required, you can use the syntax of expression-generators, which are discussed in
article 4 of the cycle .
3 Sorting collection items
Sorting elements of the collection is an important and demanded function that is constantly encountered in ordinary tasks. There are several features that are not always focused on, but which are very important.
3.1 sorted function ()
We can use the sorted () function to list the sorted elements of any collection for further processing or output.
- the function does not change the original collection, but returns a new list of its elements;
- regardless of the type of the original collection, the list (list) of its elements will be returned;
- since it does not change the original collection, it can be applied to immutable collections;
- Since when sorting returned items, it doesn’t matter to us whether the item had a certain index in the original collection, it can be applied to non-indexed collections;
- It has additional optional arguments:
reverse = true - sort in reverse order
key = funcname (starting with Python 2.4) - sorting using the special function funcname, it can be either a standard Python function, or a function that you have specially written for a given task and a lambda.
my_list = [2, 5, 1, 7, 3] my_list_sorted = sorted(my_list) print(my_list_sorted)
An example of sorting the list of strings by the length len () of each element:
my_files = ['somecat.jpg', 'pc.png', 'apple.bmp', 'mydog.gif'] my_files_sorted = sorted(my_files, key=len) print(my_files_sorted)
3.2 reversed () function
The reversed () function is used for sequences and works differently:
- returns a list generator, not the list itself;
- if you want to get not a generator, but a ready list, you can wrap the result in list () or use the slice [:: -1] instead of reversed ();
- it does not sort the elements, but returns them in reverse order, that is, reads from the end of the list;
- from the previous paragraph, it is clear that if we have a non-indexed collection - we cannot derive its elements in the reverse order and this function does not apply to such collections - we get “TypeError: argument to reversed () must be a sequence”;
- does not allow the use of additional arguments - there will be an error "TypeError: reversed () does not take keyword arguments".
my_list = [2, 5, 1, 7, 3] my_list_sorted = reversed(my_list) print(my_list_sorted)
3.3 List methods. sort () and. reverse ()
The list (and only it) has special methods .sort () and .reverse () which do the same thing as the corresponding sorted () and reversed () functions, but at the same time:
- They change the original list itself, and do not generate a new one;
- Return None, not a new list;
- support the same additional arguments;
- they do not need to pass the list itself as the first parameter; moreover, if you do this - there will be an error - an incorrect number of arguments.
my_list = [2, 5, 1, 7, 3] my_list.sort() print(my_list)
Note: This is a common mistake of beginners, which is not an error for the interpreter, but does not lead to the result that they want to get.
my_list = [2, 5, 1, 7, 3] my_list = my_list.sort() print(my_list)
3.4 Features of dictionary sorting
Sorting the dictionary has its own characteristics, due to the fact that the dictionary element is a pair of key: value.
UPD: Also, do not forget that speaking of the sorting of the dictionary, we mean the sorting of the data obtained from the dictionary for output or save to an indexed collection. Saving data in the standard dictionary itself will not work; they are in it, like other non-indexed collections, in random order.
- sorted (my_dict) - when we pass a dictionary to the sorting function without calling its additional methods - only the keys are searched, the sorted list of keys is returned to us;
- sorted (my_dict. keys ()) - the same result as in the previous example, but spelled out more explicitly;
- sorted (my_dict. items ()) - returns a sorted list of tuples (key, value) sorted by key;
- sorted (my_dict. values ()) - returns a sorted list of values
my_dict = {'a': 1, 'c': 3, 'e': 5, 'f': 6, 'b': 2, 'd': 4} mysorted = sorted(my_dict) print(mysorted)
Separate difficulties can be caused by
sorting the dictionary not by keys, but
by values , if we do not just need a list of values, and it is to output pairs in the sort order by value.
To solve this problem, as a special sorting function, you can pass the lambda function
lambda x: x [1] which of the tuples obtained at each stage (key, value) will be taken to sort the second element of the tuple.
population = {"Shanghai": 24256800, "Karachi": 23500000, "Beijing": 21516000, "Delhi": 16787941}
UPD by ShashkovS : 3.5 Additional information on using the key parameter when sorting
Suppose we have a list of tuples of names of parts and their values.
We need to sort it first by the name of the parts, and the same parts in descending price.
shop = [('', 1200), ('', 1000), ('', 300), ('', 100), ('', 1500), ('', 12000), ('', 2000), ('', 200), ('', 2700)] def prepare_item(item): return (item[0], -item[1]) shop.sort(key=prepare_item)
Sort result for det, price in shop: print('{:<10} : {:>5}.'.format(det, price))
Before comparing two elements of the list, the prepare_item function was applied to them, which changed the sign of the cost (the function is applied exactly once to each element. As a result, with the same first value, the sorting by the second occurred in the reverse order.
In order not to produce utilitarian functions, instead of using a third-party function, the same effect can be achieved using the lambda function.
Additional details and examples of using the key parameter:
UPD by ShashkovS : 3.6 Stability Sort
Suppose you need to sort the data first by column A in ascending order, then in column B in descending order, and finally in column C again in ascending order.
If the data in column B is numeric, then using the appropriate function in the key, you can change the sign of the elements of B, which will lead to the desired result.
And if all the data text? There is such an opportunity.
The fact is that sorting sorting in Python is stable (since Python 2.2), that is, it does not change the order of “identical” elements.
Therefore, you can simply sort three times according to different keys:
data.sort(key=lambda x: x['C']) data.sort(key=lambda x: x['B'], reverse=True) data.sort(key=lambda x: x[''])
More information on sorting resilience and examples:
wiki.python.org/moin/HowTo/Sorting#Sort_Stability_and_Complex_Sorts (in English).
I invite you to discuss:
- If I somewhere made an inaccuracy or did not take into account something important - write in the comments, important comments will later be added to the article indicating your authorship.
- If some points are not clear and clarification is required - write your questions in the comments - or I or other readers will give an answer, and efficient questions with answers will be later added to the article.