 In this video, we're going to see how we can use lists together with common operators. So let's create a new file. Let's call it ListOperations. So whenever I speak of an operation in my course, then I typically mean that we use some operands and connect them together with an operator like PLUS, for example. So let's make an example. Let's create a list of names, just as we did in the previous video. And let's put in there the names, for example, Bertolt, German name, but also Oliver, also German name, but also international name, of course, and Karl with a C. So now we have a list with these elements as strings. In memory, this would look somewhat like this. So we have a list object, and by now we know that list objects have a little bit more space in them because that allows us to append some stuff at the end without having to create new list objects. Now let's go ahead and abbreviate that a little bit. So the first element, for example, would be Bertolt. So it would be a string object. I simply write it here as B. Let's go ahead, create another string object here, and let's take another string object here, this would be C. And we call the list names, so let's simply go ahead and make a variable called names from the global scope, simply reference the list object here. So what could we do here, using operators? So one thing that we have already seen in chapter one is that we can take a list and we can add to that, for example, another list. So that may strike you in a weird way. So how can I add lists together? Does that even make sense? So let's use a second list and let's put in the names, for example, Dietrich, and another German name, which would be Yves, here just to have a variation of a couple of names. And the answer to the question, can two list objects, so the list names and the other list object here, can they actually be added together? And the answer is yes, and what we get back is another list object here, with now five elements in there. So when I executed this cell here, names plus a new list, Dietrich and Yves, let's see how that looks like in memory. So first of all, Python reads here from left to right, therefore it reads the variable names first, this is simply a reference lookup, then it says plus, and then it has to, on the right hand side, first create a new list object on the fly, and on the fly simply means we create it without giving it a name. So let's go ahead and create another list object here, on the fly, so to say. And the first element in there would simply be Dietrich, so let's abbreviate that with a D, and the second one, the second element would be Yves, so let's abbreviate that with a Y. Now we have two list objects, and now we are concatenating them, that is the technical term concatenation, we've also seen that when we talked about the string data type in chapter six, so I'm now going ahead and I want to add this list with this list here, and what Python does is, the list, the addition operator, it simply goes ahead and asks the left hand operand, so this list here, hey, can you add yourself to this one here? And then the list object says, well, this is also a list, I know how to do that, and then the two of them go ahead, and what they do is, they are going to create a third list object, and now the third list object will be created like that, the first element will be the first element from the left operand, so we will simply get a reference to here, then we get a reference here, and another reference here. The same holds true for D and Y, so we get references right here, however, these are not the first and second references, these are here references four and five, right? And now this list object here is basically the return value that is given back to us, so I indicate that with a red dotted line here, so that is what we see below the cell, and I could have stored that in a variable, maybe let's do that and call that moreNames equals, or set it equal, or assign to it the result of that, and now moreNames would be this longer list, and that would mean we create a new variable here, let's call it moreNames as we did in JupyterLab, and put a reference right here, so whenever you remember that from the very first chapter, whenever I put a red dotted line going from right to left, it simply means we get back a reference to this object, and then it depends on us what we do with the reference, here I store it in the variable moreNames, and now this list object is going to survive, and this list object here is not going to survive because it has no further reference going to it, so therefore the garbage collector is going ahead and will simply remove this list object here, and the references, these two string objects however, they are going to survive, and they have a reference from this new list object. So what do we learn from that here? So first we can add two list objects together, we call that concatenation, and also what we see here from the diagram I drew is concatenation also is very much similar to the concept of a shallow copy, so now we have the old, the original names list with three references, and we have a new list here, moreNames, with also a couple of references, and these three references reference objects that are also referenced by the names list, right? So these three objects here, they are shared between the first and the second list, and that is similar to the concept of a shallow copy that we saw a couple of videos ago, okay? So let's continue, what other operators are there? Well, just as we did with string concatenation in chapter six, well there's also a multiplication operator, so let's say I say two times names, and I get back Bertolt, Oliver, Karl, Bertolt, Oliver, Karl. So how does that look like in memory? So how that looks like is as follows, a new list object is created on the fly, just like that, and now let me maybe use a different color so that you can still recognize where something is going. So the first reference is of course going to be because that is the first reference of the original names list and I'm multiplying two times names here, so that is why we take all the references from this list here. So the second reference is going here, the third reference is going here, and now you may wonder where is this reference here going? Well, this reference is of course also going here, and this one here, and this one here. So now we have those three objects here, B, O, and C, the string objects, they have now many references going to them. So that is what happens here. So when we multiply a list, we get back a new list object where the references are taken from the list object that is being multiplied, and then the references are simply repeated. So what we learn from that is that Python is a very memory-efficient language, so all the operations in this video that we saw so far, but also the example I showed you in the video Shallow vs. Deep copies, they show you that whenever it's possible, Python does not copy the actual data object, so data objects meaning the string objects here with the actual data in it, so whenever Python can do so, it simply goes ahead and copies all the references, and nothing but references. However, this is also similar to the concept of a shallow copy, so we must be careful here. Let's assume that B, O, and C were themselves mutable objects, so they are string objects, they are immutable, but let's assume that they are mutable. So what would that mean if I change, if I went via this variable here to the first reference here and I made a change to that object here? Well, I would also see the same change in the fourth spot here, in index number three, right? Because index number zero and index number three are really pointing to the same object. So whenever these objects here, the data objects, they are immutable, and the most important immutable data types that we know are of course the string data type, but also all the numeric data types, then everything will be fine because we can never make a change to that. However, if we use a NASA data structure, and we have, let's say, a list here, or maybe some other data structure that can be mutated, then we have a big problem. If we make a change via the first reference, we would see the very same change via the fourth reference here, right? So we have to be careful here. And this all goes back to the video shallow versus deep copy. So if you understand shallow versus deep copies, then you should also understand everything that is right here. And all the details that I'll show you in the videos, as a beginner, most likely you're not going to need to know that, but after a while when you write bigger programs, you will run into problems where some outcome of some algorithm is not what you expected to be. And then one fun thing you should have in your mind is that Python at many places makes shallow copies or things or operations that are comparable to a shallow copy, so copying only references and not the data themselves. And whenever that happens behind the scenes without you knowing, then maybe that could be a source of a semantic error in your programs, right? So I want to plan that in the back of your head if you want to continue to become good at Python and maybe want to go into a data science career, then you should really focus into these details as well because at some point not knowing that will bite you. Okay, so let's see what else is there to be said. So of course, obviously we could also multiply names from the right and not only from the left with a number. So now we repeat the same elements three times and that is the two operators plus and multiplication that are supported not only by numbers, but also by sequences in general or in abstract terms. So remember how concatenation also worked for strings and now it also works for lists here and that is not an accident. The reason why concatenation works for both strings and lists is simply because both strings and lists are examples of sequences. Okay, the abstract data type or the abstract concept of a sequence is basically what is behind all that. So concatenation works for sequences in general usually and lists and strings are simply examples of a sequence and therefore concatenation also works for these data types. Now let's look into something that is not strictly speaking an operator but that looks like an operator and that is very valuable to know and you will see it all the time in Python code. So this is, let me give it a new header, unpacking and there will also be the opposite of that called packing in future video. So what is unpacking? So let's assume you want to create a new list and the new list is going to be a list of names because it's just the example here. So let's say the first name in the list is going to be Achim, a German name and the last name will be Xavier. That's probably an international name also works in German. So now let's assume that what you want to do is you want to put the names from the names list so the names Bertolt, Oliver and Karl you want to put these three names inside this list here. Well what you could do of course what we saw in the previous video you could simply refer to the names list here. However then we don't have a flat data structure we have a nested data structure, nested meaning some elements of the outer list contain a reference to some inner list that itself contains the data. So that is a nested data structure but what we want to do instead is instead of creating a nested data structure I want to do the opposite I want to create a new list with flat data here a programmer to use the term flat what they mean is they mean that there is only one object one container that holds all the references to all the data objects in a flat way. There is no hidden layer where you have to grab into in order to get to some deeply nested data piece. So how can we do that? How can I get rid of these lists here? This list syntax here. So there is a syntax in Python called the unpacking syntax and we will simply write star operator here on asterix. So these star names see maybe I leave that here in as an example so that you can refute later and maybe also see the difference right away if I put the star right here what you see now is that we have the same names in one list object but there is no inner list object. So we have flattened the data and in a future exercise I think it is in chapter 9 when we once we talked about mappings there will be an exercise in the book where one of the tasks is you are given nested data and you have to make it flat and what we mean by flattening the data is by simply making sure that you only have one container object that holds all the data in a flat manner. And strictly speaking this here is not an operator this is some literal built-in syntax that looks like an operator but it is not comparable to a star operator in this context here in this context having a left and a right operand that is when the star operator is really an operator but written in this way this is really not an operator but the unpacking syntax but it's just a minor detail don't worry about that I just put it inside this video because it looks very much the same and it's worthwhile to know you often see that when you call functions and you want to unpack let's say some container and you want to pass the arguments in a flat way then you often see that in function calls for example but we will see that in future videos a couple of times so just understand that this just unpacks the data in the container okay so maybe another way for those of you who still don't get this is to write it in a manual way so to say so this is the same so this line here is the same as if I use the index operator just like that so let's copy that let's copy that like that and let's copy it one more time this would give me the same result however often times when you work with lists like data you don't know how many elements are in a list so here I know that there are three names in the names list therefore I can fully enumerate all of them by saying names 0, names 1, names 2 but sometimes the list has like a million entries a million elements and then you couldn't write that with your hands so this is just a manual so to say way of writing everything out but the unpacking syntax basically does that here for you it basically automates the indexing here so worthwhile to know so now let's finish this video by talking about another operator or another set of operators namely the comparison operators that we saw the first time in chapter 3 when we talked about the so-called relational operators relational operators are all the operators that compare a left and a right operand and that also works in the context of lists of course so let's do the following I'm going to use my names list ideally and let's copy paste what we see below the cell here and let's go ahead and let's take the names list and compare it regarding equality to what I just copy pasted here and maybe if you want replace the single quotes with double quotes as you know that I personally prefer the double quotes but you could also simply leave the single quotes wouldn't make any difference so now we get back through why because I have a list on the left hand side and I created a new list on the right hand side on the fly and both lists contain the same elements or elements with the same value that is important in the same order okay and if that is the case then both lists compare equal let's go ahead and check what happens if I add another element here on the left hand side let's say I go ahead and I add Carl with a K and now I get back false why because the list on the left hand side obviously has not the same number of elements than on the right hand side and then therefore the elements are just different also if I go ahead and compare names to the actual names list here and I change Carl with a C into Carl with a K I of course also get a false okay now let's look at another operator this is now equality comparison but let's also look at a couple of others for example just to remind you that it exists the unequal operator in python we are going to write that as exclamation mark equals this is now going to return the opposite of the of the equality comparison operator since that here was false this here must be true because it's the same expression and the only difference being instead of equality we are checking for inequality so these are the opposite cases and now you may wonder how do the smaller than operators work in this context well let's do that let's copy maybe this cell here and let's go ahead and ask the question is the left hand side smaller than the right hand side and intuitively this is true and the reason why is because they are all equal except for the last entry here and if the list on the left hand side contains an entry all the entries are the same on the right hand side except for it does not contain all the elements then it is smaller but also note how let's copy paste that instead of comparing that to a list with more elements I compare that to a list maybe let's put names above here one more time so if I compare the names list with a list of the same size but Carl here is not written with a C but a K then this also gives me back true why? because Python would go ahead from left to right and compare a bearthold so this bearthold with this bearthold and this Oliver with this Oliver compares Carl here with a C with this Carl with a K and Carl with a C let's also write that here Carl with a C is smaller than it is strictly smaller than Carl with a K because of how string comparison works and therefore because of that result the entire list is also considered smaller than the other list here okay and of course if you do that the other way around so let's maybe do another example let's copy this list here and let's say as the question is that smaller than Carl or the names list that has the Carl with a C in it now we get back false why because Carl with a K is now not smaller than Carl with a C right? it's it comes after Carl with a C so that is also how you can use comparison so the exact rules of how these operators work are exactly the same as we saw in the chapter on string comparison so first of all in other words to summarize that Python takes both sides the left and the right hand operand and it goes in a stepwise fashion it compares element by element and once the first pair of elements when comparing in a pairwise fashion is not equal then a decision has to be made whether or not the left hand side is smaller or greater than the right hand side and the decision that is made will be done according to basically the rule that has to be applied when the two objects are simply compared to each other so and then in the rare case where all the elements are equal and let's say one of two lists has one more element then automatically the list that has one more element is considered greater it's intuitively so this is how we would interpret this sign here this symbol in an intuitive way just as well okay so that is operators so relational operators work for lists a couple of arithmetic operators work for lists as we saw they do concatenation please keep in mind that the way this concatenation is done follows the same logic as we saw in when we talked about shallow copies and also the unpacking syntax here is quite nice to know it's not strictly an operator but you could actually view it kind of as an operator here okay so that leaves us here with this video when we talked about list operators and operations and in the next video we are going to see further details about what to look out for when working with list objects so I see you then