 Python has another collection type which it calls a tuple, and a tuple really is just like a list, except it's immutable. So you create a tuple that has some number of items in a set order from index 0 up to the last item, but once you create the tuple you can't change it, you can't modify one of those values, you can't add any new values, and you can't remove any existing values. The term tuple, by the way, comes from mathematics. To create a tuple we have a literal syntax, very much like we have a literal syntax with lists, except instead of square brackets we use parentheses. And that's frankly confusing because parentheses are already used to denote the order of operations in an expression, or to just surround any arbitrary expression for no particularly good reason just for clarity, and of course parentheses are also used to invoke a function. So it can be tricky at first to recognize from context when you're looking at a tuple rather than say the arguments of a function call. And it also means that Python has to have a special rule for when you have a tuple with just one item. Here we can see in the top example a tuple with three items, first the number three, a string reading high, and the number negative one. But then in the second example we have a tuple of just one item and we have to write a comma after the item. Otherwise it would be mistaken for just parentheses with a three inside, which is just an expression that returns that value. So parentheses with three comma inside is a tuple of one item, the value three, whereas parentheses with just the value three inside, that's just the value three itself, not a tuple. And lastly if you see an empty pair of parentheses and they're not the parentheses of a function call with no arguments, then well that's just a tuple that's empty. It's a tuple with no items. Now you may be asking what's the point of tuples because we have lists already and lists are just like tuples, except you can also modify them which sounds better, right? Well for certain purposes, especially in cases where you're dealing with smaller sequences of items, you generally think of them as being a fixed set of values, a fixed sequence of values. So in those cases you may want to use tuples because it sort of ensures that down the line the sequence doesn't get mutated. Really it's almost kind of a stylistic choice. You'll find that tuples are most commonly used for small groups of related items. For example, if you have a pair of coordinates, an x and a y coordinate, those are two separate numbers, but you can group them together as a tuple and that sort of spares you from having to create a separate point class that has a x member and a y member or something like that. So that's one common scenario where a tuple is used to sort of an ad hoc simple compound data type. The various collection types of Python are broadly grouped into two main categories, what are called the sequence types and what are called map types. Sequence types are collections where the items of the collection have a sense of defined order. This item is the first item at index 0, this item is the second item at index 1, and so on. The map or mapping collections, as they're sometimes called, have no defined sense of order between their items. Rather a map is a collection of key value pairs. So of the types we've already seen, lists, tuples, strings, bytes and byte arrays, those all fall under the category of the sequence, whereas dictionary is the only type that falls under the category of map. And whereas there are actually a couple sequence types we haven't seen yet, dictionary really is the only built-in map type found in Python. Yet in Python we still talk about a map as if it's a broad category of types, even though we will only have just the one because conceivably in your own code you might define your own classes, which are collection types that operate like a map, they're a bunch of key value pairs. So for that purpose it's still a useful concept to talk about maps, even though most of the time the term only applies to dictionaries. What the real significance of these two terms sequence and map come down to is that they define for some broad set of types a common set of operations. Broadly speaking, all sequence types will have the operations a, b and c, and all map types will have the operations x, y and z. So looking first at sequence types, these are the operations which pretty much all Python sequence types will have with the caveat that immutable sequences will be missing some of these operations for the obvious reasons that some operations will modify the sequence and obviously those can't apply to the immutable sequence types. So going over these briefly, first at the top you'll see a number of special methods, most of which are operator methods like add and mole for example are really just the methods invoked when you use the plus symbol operator on a sequence or when you use the multiplication operator on a sequence. And first off what the add method does with two sequences is concatenate them. So say you have two lists, one the first with three items, the second with two items, you use the plus operator with both those lists as operands and what you get back is a third list, a new list that contains five items. First the three from the first operand and then the two from the second operand. And this is just like we've already seen with strings because strings again are a sequence type. So use the add operator with two strings, you get a concatenation of the strings. And we already briefly discussed what mole does, the multiplication operator does with strings. The second operand is an integer and it returns the multiplication of that string, meaning basically the same string concatenated with itself n number of times. So the string foo times three returns a string reading foo foo foo, foo written three times. And likewise used with other sequences like a list of three items, if you multiply that times four then you'll end up with a list of twelve items, the same three items repeated four times. And this multiplication operator just like with strings doesn't actually modify the list, it doesn't modify the existing list, it creates a new one. The get item methods, set item methods and del item methods as their names imply will retrieve an item from the sequence, set an item from the sequence that has modified some existing item in the sequence, or delete an item from the sequence. And as we'll see later there's an operator for invoking these methods and that of course is what is usually used rather than invoking these methods directly. And set item and del item of course will mutate the objects, of course they only work on the mutable types like lists rather than tuples and strings. The contains method will return true when the argument is an item which is found in the collection, in the sequence. So I pass the number three to contains and then it searches through the sequence to see if there's an item equal to three and be clear that contains uses an equality test, not an identity test, just some value equal to three, not necessarily the exact same object itself. And contains also is a method which is usually invoked with an operator. The lend method we've seen before, it returns the number of items in the collection, in the sequence. And usual practice is not to invoke this method directly but rather to pass the object to the built-in function lend. That is just len, not double underscore len, double underscore just len. The index method we've already seen with string, you pass it an argument and then it checks to see whether that object is found somewhere in the collection and if so it turns the index at which it is found. The count method also searches for the occurrence of an object in a collection except it returns the number of times it occurs rather than the position where it's found. So you might have the same item three places in the same list and so the count method when passed a value equal to that item it will return three. The remove method also searches in the sequence for an item which is equal to the argument which you pass to remove except whereas index will then return the position of that item, the index, remove will simply remove it and if it's not found in the sequence then remove will throw an exception. The append method will append the argument you pass to it to the end of the sequence like with a list we've done this many times, we append an item to the end. The extend method also appends to the end of the list except it takes this argument another sequence and it appends the individual items of that sequence one after the other to the end of the sequence. So in a way extend is like concatenation except we're actually modifying the original list so this of course only works on mutable sequences. The insert method takes an index and a value and it inserts that value at the specified index. The pop method will return and remove the last item in the sequence so it's just a more convenient and efficient alternative to first get in the last item with get item and then using the remove method. The reverse method will reverse the order of the items in the sequence and it does so not by returning a new sequence but actually just by mutating the existing one so of course it only works on the mutable sequence types. And finally the sort method will sort the items in a sequence and again this is done by actually mutating the sequence so it only works on the mutable types. As for maps first off we have most of the same special methods get item set item, dell item contains and lend and what they do is fairly evident just the same thing applied to a map instead of a sequence. The only subtle thing to keep in mind is that the contains method will search only amongst the keys of the map not the values. As for the methods unique to maps first we have clear which will as the name implies clear the contents of the map it'll basically get rid of all the items all the key value pairs. We invoke the from keys method by passing to a sequence and then what we get back is a new dictionary a new map containing just those items from the original with keys specified in the sequence. The get method works like get item except rather than throw an exception if the key isn't found it'll just return a default value the value none unless you actually pass to get a second argument which is then the default value returned when the key isn't found. The items method will return a sequence of all the items in the dictionary and it does so in the form of tuples where the first item in the tuple is the key and the second item is the corresponding value. The keys method will return a sequence of just the keys and the values method will return a sequence of just the values. And the important thing to understand about items keys and values is that because maps have no concept of order of their items the items keys and values you get back in these sequences are in no particular order. The pop method does the same thing as get except if the key is found it will then remove that item from the map. Pop item is just like pop except it returns a tuple of the key value pair of the whole item not just the value. The set default method will return the value of the specified key but if no such key is found it will set that key it will assign to that key and by default it assigns the default value of none to that key but you can also pass to set default a second argument which is then the value assigned in the case where the key isn't already found. So if a dictionary already has this key just get me its value otherwise assign it this default value. And lastly the update method you pass to it a map and then the items of that map are assigned into the object of the method. So if the argument map has a key string foo with the value 3 then the object map has its key string foo given the value 3. So if it already had a key string foo then it's simply updated with that value the value 3 otherwise if it didn't have it already then it's given this new item this new key value pair. Now I mentioned that get item set item contains and del item are all operator methods so here they are in operator form. The expression x subscript y is equivalent to x dot get item with an argument of y and if you assign the value z to x subscript y the same as x dot set item with the arguments y and z and y in x in this a reserved word operator that is equivalent to x dot contains y don't get too confused by the reversal of x and y and finally del x subscript y that's equivalent to x dot del item y and the thing to keep in mind about del though is it's not actually an operator it's actually a kind of statement like assignment is a kind of statement but if you see del used it's always used at the front of the statement now when I went over the sequence operations I didn't mention that they also have relational operator methods the methods for less than less than or equal to greater than and greater than or equal to and these are the methods respectively LT, LE, GT and GE and what these relational operators do with number types is very evident 3 less than 5 returns true because 3 is less than 5 what does it mean for one sequence to be less than or greater than or equal to another so here's actually how python defines that behavior the general idea is that python goes through the corresponding pairs of the two sequences that is starting from index 0 in both sequences it compares those two values and then moving on to index 1 it compares those two values the values of at index 1 of both sequences and it does this through the whole sequence until it finds a pair which differs and that first pair which differs is used to determine which sequence is greater than the other obviously if you get to the whole sequence and it turns out that all the pairs match well then the two sequences are equal neither is greater than the other so here we have in the top example a greater than operation with two sequences first 5 and 7 and then 5 and 4 and python does the comparison by going through the corresponding pairs finding the first that differs 5 those values don't differ and then 7 and 4 well those values do differ and 7 is greater than 4 so the sequence with 7 is greater than the other the left sequence is greater than the right sequence so this operation returns true in the second example again python finds the first pair that differs and well that's actually the pair at index 0 here 5 and 3 they differ and 5 is greater than 3 so the left operand the left sequence is greater so this operation is false and lastly here the same thing but with a less than or equal to operation well again 5 is neither less than or equal to 3 so again this is false and be clear that for the sake of these comparisons two sequences are considered equal only when all of their values match and when they have the same number of values so here comparison of equal lists with either less than or greater than will return false because neither is greater than or less than the other with the less than or equal to or greater than equal to comparisons both return true because the two sequences are equal when however we're comparing two sequences where they have all the same matching values except one is longer than the other well then the sequence with more items is always considered greater so the list 5 and 7 is always less than the list 5 7 negative 9 and last thing to understand is that when the items of the sequence are compared will well is quite possible the items of the sequence are themselves sequences and when that's the case well they're compared to the same manner so effectively the comparison is done recursively here when we do a less than comparison on the two sequences well the first items in both of these sequences are themselves sequences so we do a less than comparison on those two sequences and we find that well no 3 9 is not less than 2 5 so already we found that the left operand is not less than the right operand and this returns false lastly with the relational operators strings again are also a kind of sequence and so they have defined behavior with these relational operators and the idea is really the same the two strings are compared item by item until the first pair that differs is found and then that's used as the basis of the comparison and of course the items which make up a string are the individual characters the course and then is that how does python compare a pair of characters and the answer is that it does so by meaning code code points so in the first example here is hello less than hi well it's the second characters of the string which are the first to differ is e less than i yes it is because the code point of lowercase e is less than the code point of lowercase i so this operation evaluates true in the second example is hello greater than hell yes it is because the two sequences match except hello is longer and then is hello less than or equal to hello yes of course it is because the two are equal is hello less than or equal to hell no because hello was actually greater than hell