 In this video, we are going to learn about the so-called scoping rules that are involved when calling a function. To do that, let's create a new file and rename it into Scoping Rules. Let's revisit the function that we developed in the last video. The idea is that we go back to the very first example of this course. We are given a list of numbers and we want to find the average of all the even numbers. Let's write a function for that. If you want the details, just go one video back. In this video, we are just going to write down the function as it is. We use the dev statement, give the function the name AverageEvents. The function takes the parameter list and we call the parameter simply Integers. Then we are going to write a so-called doc string using the triple double quotes notation. We are going to write a subject line, calculate the average of all even numbers in a list. Then we are going to specify the parameters the function takes. It's going to be an integer parameter and integers should be a list of ints. So we also specify what data type the function expects. These are just the whole numbers to be averaged. Then the function returns simply the average as a floating point number. Then we have to write down the code. What we are going to do inside the function is to indent it by four spaces. We are going to develop a list comprehension as we saw previously. We say we keep n for n in integers. If n modulo divided by two is equal to zero. In other words, if n is even. We sign that to a variable called evens. We always want to give our lines in the code a descriptive name. Using a good descriptive variable name is the best way to do that. In the next step we calculate the average as the sum of all the evens using the built-in function sum. And also the built-in function len for the number of even numbers. Then we must not forget to write down the return statement. The function has to return something back. So to say the output of the function. We simply go ahead and we will write return average. If we don't write down the return average then the function is not going to return anything back. And that means the function does some calculation but we don't get any result back that would be not so good. So let's define the function. And now let's also go ahead and create a list called numbers. And let's assign to that the numbers we are basically always using. So the numbers are seven, eleven, eight, five and three. Twelve, two and six, nine, ten, one and four. So these are all the numbers from one through twelve without any particular order. And now we simply can go ahead and call our own function by simply writing its name, average evens. We use tap completion if we don't want to type other characters. Then we put down the call operator, open closing parentheses. And then we have to give the function an argument. So what we put in when we call the function is called an argument. That is why it says arcs here. But from within the function when we define it we refer to integers as the formal parameter. So we are going to give the function numbers as the argument for the parameter integers. And the function gives us back six point eight and that means I must have forgotten some number and indeed if I watch closely here we forgot the number eight. So let's go ahead and we get back seven point oh. That also reminds us that we should always call our function with input for which we know the output in our head so that we can actually check if our function is free of any semantic errors. So that is how we specify the function. And now we are of course interested in the so-called scoping rules that regard the function. So let's maybe also create a nice title here. The scoping rules and the scoping rules what they do is they concern when variables are visible. So basically let's simply write that down. When is a variable visible? That is the question we are going to answer in this video. So we have a name called average events and obviously we can refer to it. When we type average events, average events is found. The same holds true for numbers. However, if we go ahead and let's say outside the function so after I executed the function let's say I want to refer to the variable called events then we get a so-called name error. This is an example of a runtime error so you always want to remind yourself what three kinds of errors there are. So there's one group which is syntax errors whatever ever run. We will always get an error message for runtime errors. The code is syntactically correct so it should work. However, given the circumstances of the running program some error occurs and in this case by simply writing down events Python knows what to do. However, the name event has not yet been created so that is why this belongs to the category of runtime errors. And then the third kind of error just to remind you is the group of semantic errors which we can only check by looking at the input-output relationship of a function. So now we have as we see a name error Python does not know what events is. So however we define events so that may look weird for some of us but there seems to be a pattern and the pattern is the variable is defined within the body of the function and therefore it is not visible in the outside world. Let's confirm that by also asking Python hey, do you know what the variable average means? And also here Python says well, it's a name error. And then we must not forget the formal parameter so remember in the last video I purposely gave the parameter the name integers and not numbers so that we have a different name for it just to make a point and integers the formal parameter that lives within the function also does not exist here. So if I ask Python hey, what if integers we also get a name error. So both all the temporary variables set inside the function while it is being executed but also the parameters are not visible outside the function that means after the function call has been made and also of course not after the function has been defined. So the question is how should we think of the life cycle of these names? And let's do that let's talk about that using a memory diagram. So when we define the function what is going on is we are going to create an object with some code in it and the code are simply 1s and 0s so we don't have to really care how this is exactly modeled here but there are 1s and 0s inside the object that basically resembles the code that we just defined in the function. The function has a certain type and we saw that the type is function. We could also go ahead and confirm that by simply asking the question here what is the type of average events and we see that the type is indeed a function. So this is why I put the type function here and then we have a name and the name is called average event so that is as we see in the list of all the names that are available we have a name average event and that refers to the object that contains the code. So this is what happens after we define the function. So far in my memory diagram we have not yet executed the function. And now we see what is going to happen if we execute the function. So let's go ahead. So as we execute this function is as follows Python creates a new area on the left hand side where all the names are stored and this basically belongs to this function. So maybe we can go ahead and make an orange arrow here just to show that this area on the left hand side in the memory belongs to the function. And now what's going to happen is as far as in the numbers argument maybe let me also put the numbers argument in the memory so let's have a list object we remind ourselves what list objects look like in memory so list object are just those big chunks that hold references to other objects which are the numbers I only draw them here very small just to remind yourself and here is a variable let's call it numbers this is also there in our application here in our example but as we see numbers it lives outside the area with all the local names to the function and now as the function is being executed so in other words as we go ahead and execute this code cell here average events and we call it with numbers as the argument that is when Python goes ahead and creates the parameter name called integers and makes that refer to the same list that we pass in as the argument so this is what happens when the call starts at the very beginning before any code inside the function here any code runs so the only thing that has basically happened so far is we created a new space in memory and we refer to these spaces as namespaces rather simple term and we write in there all the parameters that the function defines and now what we do is the arguments or the inputs to the function they are given to the function only as references so remember the video who am I and how many showed you how I could actually try to fool you by giving two names to the same list and then we change the list via one variable and we saw the changes and maybe back then in this other video you thought maybe theoretical why would I care about this, well as we see every time we call a function we quite naturally have two references to one object to the same object so there is always a reference to the object outside the function and one of them inside the function and then as the function continues to run then happens is the first line in the function is going to run the right hand side is going to be evaluated and that will create a new list object with only even numbers and then once that is done that new list object is assigned to the variable called evens so let's go ahead and see what happens after the first line is done somewhere in memory we get a second object also a list object so maybe let me write list here and here as well and this second list object also has the number, has references to the integer objects and now here we only have even numbers so that is why the second list object should be a little bit smaller than the first one assuming that we have even and odd numbers in here and here we only have even numbers so the second list object will be smaller and then what's going to happen is python will go ahead and will create a new name called evens inside the namespace that belongs to the function and will make that reference this list object ok so this is what happens after the first line has been executed in the function and now we go on and we execute the second line in the function body and what this is going to do in this example after the sum and the length functions have been executed and the results are divided we get a result and the result obviously is the number 7.0 in the example and this is of course of type float and we will create then a new name in the namespace called simply average and this is going to refer to this object ok that is what happens after the second line in the function has been executed and now let's continue how comes the last line in the function the return line the return average line and so now what this is going to do it's going to do the following it is going ahead and it will take so the return statement takes one expression and the expression happens to be just the reference to the variable average so in other words let me use a different color python will go ahead and will take the reference to the 7.0 I mark it as in red here and this is going to be the only thing that survives after the namespace goes away so that is what happens after the function is done the entire namespace and everything that is inside it will be removed from the left hand side where we find all the names ok and then as we have learned in the previous video all the objects that no reference going to them will be eventually collected by the garbage collector and that of course means that the intermediate list object called events with only the even numbers that is of course going to go away this is going to be garbage collected sometime maybe not immediately but at some point in time this will be removed and the only thing that survives is the 7.0 ok and of course also maybe just to be very precise to the outer numbers list will also be removed and now the question is what happens to the name average and the reference to the 7.0 so this depends it depends here on what we do in the code so what we did here is I simply call the function and I did not I did not assign the result to anything but let's assume I assigned the result to a variable let's call it simply result so now the return value will be assigned to a variable called result and let's now see in the memory diagram what that would do what that would do is that would create a new variable called result of course and result is going to reference whatever average reference before which is the 7.0 and of course the name average is also going away however the reference that was under this name this is basically the only thing that survived after this name space goes away and now we already saw in Python Tutor the online tool that we also use to draw memory diagrams in an automated way they on the left hand side where we say names they also call that frames and the reason is because this name space is also called a frame in technical terms okay and there's also one frame that is always available always visible and that is what we call the global frame and the global frame are basically all the names that are not within a name space so in that case the name average evens result a number these are the global names and they are available they are visible everywhere so let's summarize what we found or what we learned so far so the first rule there will be three different scoping rules so the first rule so let's call it rule 1 I simply call the rule local scope goes away and that is what we saw in the memory diagram okay in the memory diagram the so called local scope so all the variables that are local to only one function call are removed automatically after the function call is over okay so that is what we mean with the local scope local to one function and the local scope goes away after the function is done now let's look at the couple of other scoping rules so let's go ahead and do something that you shouldn't do let's go ahead and I will go ahead and I will copy paste the function as we as we created it to a new code cell and let's call it for now average wrong so I will create a second function and let's make on purpose an error so note how we have a parameter called integers here but let's say for a simple copy paste error for example what we are going to do is instead of calling this parameter here integers or instead of referring to integers let's simply refer to numbers and let's go ahead and call the function average events or average wrong gave it a new name and let's pass to the function our as the argument the numbers list and the function gives back 7.0 so it looks like the function is correct however let's do the following let's call the function again a second time and this time let's give it a new list object that we create on the fly and let's put in there the numbers 40, 41 42 43 and 44 and that should give us back the result 42.0 but we get back still 7.0 and the reason maybe now it has now become probably obvious to some of you so within the function I want to or I should refer to the parameter the input of the function as integers however I mistakenly called the name or I refer to the name numbers numbers is not an official input to the functions not on the parameters list however numbers the variable lifts outside the function namely up here I created a global list so numbers is a global name right it can be seen everywhere in the memory and therefore it can also be seen from within the function and that is something very bad because that means the function this function the average wrong function is now dependent on a variable living in the outside world or not so in other words if this name were not to exist so for example let's call it numbers 2 just to illustrate a point it does not cause an error when I define the function when I now go ahead and call the function I get a name error because numbers 2 does not exist because numbers 2 does not live in the outside world but numbers without the 2 that lifts outside and therefore the function tries to find numbers from within the function it does not find it and then it goes one level up to the outer level to the global level and sees that numbers exist and uses these numbers so going back into our diagram what that means is when we are once we created the local scope with all the variables local to the function the function still tried to look up the name numbers it does not find it inside here but then it goes to the outer level and finds numbers here and uses it and in this scenario in the example I gave you here this is as we said an error because every time we call this function we are referring to the same list so even if I give the function another list as the input the function will still work with the numbers list it will always work with the numbers list and that's of course wrong and so now for that kind of situation I have a second rule I say global scope is everywhere that is how I call the rule so in other words all the variables that are defined outside they are always visible and can be used from within the function however it's a bad practice to do that functions should only depend on parameters that we explicitly specify and a function should not depend on anything else otherwise the function it may work in some situations it may not work in others where the outside name does not exist so always make sure that you specify parameters all the input to the function as parameters up here and then within the function you only use names that are either on the parameter list or created within the function so of course in the second line the variable event I can use because I created it one line above that is of course okay to do but don't use a name that is outside and now let's look at the third example to finish this video let's go ahead and take one more time the correct version average events copy paste it to another cell one more time and now let's do the following I will go ahead and I will rename the parameter integers into numbers okay so numbers exist outside in the global scope and it is also now a parameter in the parameters list and of course I must not forget to also rename it here so let's rename integers here also into numbers and let's just to be complete here call that average events final it's going to be our final version in this video so now let's go ahead and call average events the final the final function call it and let's give to it as the argument the numbers list it lives outside and I get back 7.0 and now let's do the same thing again it's called average events final now with a new list and let's give it the numbers 40, 41 42, 43 and 44 and for these numbers the average should be 42.0 let's see if it works and indeed it does okay so what do we learn from this last example we learn that if we have colliding names so we have a parameter inside the function called numbers and we have also still a global variable that we defined up here a global numbers variable and they both exist at the same time and python is able to tell them apart we see that with the second function call here because if python were not able to tell these two numbers names apart then we would get 7.0 here as well but we don't we get the correct answer of 42.0 and that is an example of the third rule of scoping and that is what I simply refer to as the shadowing rule but in other words if a name exists at an inner scope for example in the local scope of the function and also the same name exists in the outer scope in the global scope here then from what python will do is it will look up the names from within first so in other words let's go back to the memory diagram one more time so what this means and I'm a little bit out of space here so but let me do the following let's say python goes ahead and creates a new scope here for the function call and puts in there for example the parameter numbers and then what we have is we have numbers within a local scope within a frame but also in the global scope and what python then does it first looks from the inside out and once it finds the inner variable it uses it and it does not use the outer one and that means the inner variable numbers shadows the outer one okay so in other words the outer one we cannot see because it's so to say in the shadow of the of the inner one okay so function so variable look up in python occurs from the inside out so these are the scoping rules three of them and you should get used to them because not knowing that will create many many potentials for confusion especially for beginners so make sure you understand that and also make sure that you know what's going on here okay see you in the next video