 Welcome back, I promise you to answer some questions. Let us look at, okay, repeat execution of Python program files, I will, no, creation of files, I will demonstrate that in little while. There is one question about getting index out of range error while executing program for slide 74. You probably have the wrong data file. Do not use SSLC, I think you have to use SSLC 2 dot, sorry, only SSLC, SSLC, yeah, just the SSLC dot txt. Basically you are getting that error if there is some entry which has no fields, in which case you have to see which line it is, what it looks like, put a print statement or something like that. Anyway, so I will answer the one question that was asked again, which was, how do you create a data file? So basically the mechanism for that is simple, I will create a junk dot txt file. To do that, first I create the file object, so I call output file equals open and give it the w flag, say it is to be written, then I can say output file dot write, the only thing is I have to remember to put a new line at every line. I am irritated with the output file, my text is too big, so I will just say o is output file, o dot write. Now I say o dot save, sorry, write, sorry, o dot close, the file is now fully written. Let us look at the file, as you can see it is written the contents, so I have answered that question as well. Now let us get on to an interesting topic of functions, these are probably the most useful thing that you will learn, how to write good functions, so let us start there. So why do we need functions? Why cannot we just continue with code, writing code the way we have currently written, just keep writing the lines of code? Well, it turns out that when you are writing larger projects, you will end up writing a lot of duplicated code, you will be repeating yourself many, many times. There will be lot of things that you will need to do, common things that you will have to do and make sense to write these into little functions that you can reuse. So the idea is you reduce duplication of code, you have fewer lines of code and the scope for fewer bugs, the code is reusable, which means if you have written a function once and it is well written, other people you yourself can use it and other people can use that function. Finally, it helps sometimes when you have a function that does something for you, for example, we opened a file, we wrote lines to a file, we closed it, did we actually care about what is the details of the underlying implementation, is he doing it, is he doing it in C, what exactly is he doing, nothing, I do not care, function has been created, I can call that function, my job is done. You do not have to worry about the details if you are doing this. So it really helps in terms of what is called abstraction. Many of you are computer science people and you already understand the value of good functions. So let us see how to create functions. So let us consider the function f of x, which is essentially going to return the square of the number. Let us write a Python function equivalent for this. The syntax for this is to say def, def is a keyword, the name of the function, open parenthesis, the arguments to be passed to the function, close parenthesis, semicolon, semicolon is important as you have seen before, semicolon indicates a block. So anything indented to the right after this block, after this semicolon, is in the scope of this function. And now you say return, return is again a keyword, which returns the square of the number x star x. Now you can call this function by saying f of 1 or f of 2, so on and so forth. So let us see, let us define this as simple as that. So I will repeat, def is a keyword, def is the name of the function, x is the parameter passed to the function, return is a keyword that specifies what should be returned, a simplest function. So first question, what if the function does not require any arguments? How do you create it? Def, f, want to return something? Fine. Notice that the old f that I defined is gone, no longer available. What if I do not return anything? Let us say I just say print x, simply printing, it is not returning anything. How do you know? Well, in each of these cases, the output was stored. There is no output in this case. So for example, if I say x is f of 1, x is, so x is actually a special type called none. Type of, if I say print x, it will tell you it is none. So none is the special value that is nothing. It is like null in C or C++. None is a special quantity, capital N, west all snow. So in summary, def, function name, arguments, you can pass multiple arguments and we look at that soon. Return value which is optional. If you do not return anything, it will return none rather it is not returning anything. So exactly what I had said before, def, greet, print, hello world, greet takes no arguments and it simply prints. And implicitly, if you do not return anything, it returns none. Is this clear? If I do not explicitly say return x or return something, it will return none. The quantity it will return is none. So if a function has returned none, it means it did not actually return anything. Or you could explicitly return none for example. There is no harm in doing that. So it simply, it implicitly returns none in this case as the slide says. Now, supposing I define a function that if I require two arguments, how do you do it? Def, in this case, average a, b return a, b, sorry a plus b divided by 2. Simple. So any number of such arguments you can supply and the way you call this is average of 12, 10. Now, 12 is assigned to a, 10 is the value of b. Now the first thing that you will notice when you write, when you read some Python code is many functions have what are called doc strings. So supposing we write a function this average a, b, a, b, if the first line after that is a triple coated string, that string is treated as the function documentation. So let us try this. I am being lazy, I am just writing some sample documentation. So this can be pages, it can be a lot of documentation. This is very bad documentation, but returns this documentation nonetheless. This is a nice function. Now the nice thing is I can do a, v, g question mark. So first let us see if this works. Now if I do a, v, g question mark, notice that it shows me the function signature and also shows me the doc string. This is exactly what I typed out. So this is very, very convenient. If you write documentation for your functions immediately other people can look at the documentation to learn how to use your code. You do not have to do anything separately. But if I did not write like my f function, it says no doc string. So it is a good idea for you to always document your doc strings. So there is a question, is it mandatory to define functions at the top of a program file? No, you can define functions anywhere. You can in fact define a function inside a function, so called closures. So there are no restrictions on where you define functions in Python. So now how do you return multiple values? We have seen how to create a function, takes no arguments, takes one argument, takes two arguments, returns something, does not return something. But how do I return two quantities? So let us look at this function. We have a function circle, takes one argument which returns area and perimeter of a circle given the radius r. That is the function documentation. Here is the code, pi is 3.14, actually that is wrong. It is just some sample code. Area is pi r squared, perimeter is 2 pi r and now it returns area, perimeter. So if you can just return, this is actually what is called a tuple. This is implicitly generating a tuple internally, but you can just return any number of things as separated by commas. How do you call it? You can call it a circle 4 in this case, in which case the return values are ignored. In this case they are stored as a and p, area and perimeter of the circle. So returning multiple values is also possible. So given this, given the knowledge that you have now, can you tell me what this function does? Take a minute to read it. Take a minute to read this function and maybe from each center, the center representative can send me the answer that the center has collectively obtained for what this function does. The center representative may know the answer, but your goal is to make sure that the students in your class are able to read this code. So what does this function do? Please give me one answer per center. I will wait for 2 minutes and proceed thereafter immediately. I will give you the answer, but I want to hear an answer from everyone. Please make sure you think through your response before you answer. Someone is answering, the answer is a tuple. That is total nonsense. Essentially, if you answer something like that, this is insulting me. You are telling me all this time I have been shouting at you what function is you have not understood. What does this function do? The answer obviously cannot be tuple. Look at what one, not what two. What one only returns one argument. So do not start giving me something with two arguments. I am asking you what does this slide, what is the code doing? So anybody who answers with something like false comma two is looking at the wrong slides, which means they should be given negative marks. Okay, time is up. Many of you are not reading the code. Many of you, you have to make it a point to read code, understand it line by line. So let us go through this. This is a function called what. We do not know what it does. It takes one argument. We do not know what argument it is. I suspect it is an integer. Let us assume it is an integer. If n is negative, what does it do? It converts the number to a positive number. If it is positive, does nothing. While n is greater than 0, it will check if n divided by 2 is 1. Sorry n reminder, n divided by 2 gives a reminder or not, which means is it odd or even. If it is odd, it will return false. Many of you just think through this. What is the remaining pieces of code? Do not stop thinking there. Look at the full code. So if the function is if the value number is odd, it returns false. But what if the first number is even? So what if it is 10 for example? If it is 1, 0, n percentage 2 is not 1. What will it do? It will divide n by 10. So what is the remaining number? 1. So if it is 1, 1, 0, it is 1. 1 is bigger than 0. So the condition for the while is met. Then is 1 percentage 2 equal to 1? Yes. So it returns false. So if I have a 10 digit number, it will consecutively divide that number by 10. What happens if I divide by 10? Remember it is an integer. If it is an integer, if I divide by 10, you lose one digit from the right side. So this calculates if every digit of the integer number that is given is odd or even. If all the digits are even, it will return true. If any one of them is odd, it will return false. Is this clear? So when you read code, you should read every line. No line is redundant typically. If any digit is odd, it will return false. If any digit is even, it will return true. Wrong. It will return true only if every digit is even. So please be careful. Remember you are teaching many people. You are all teachers. So you should be able to read such code and give the correct answer and make it precise. Programs are precise. Programming is a precise art. It is not random. You have to know what every line does and you have to know it precisely. You have to know it correctly. You cannot give, ah, it may do this, it may do that. No, no, no, no. Then you may pass, you may fail. That is all I can say. So the point with good programming is you should be able to read code and understand what code does. So now that you have messed up this first one, many of you did not get it right. I will give you one more chance. What does this function do? I will give you 3 minutes and do not run the function with some crazy argument and tell me the value. Supposing n is an integer, it accepts an integer argument, what is this function supposed to do? You can assume that n is a positive integer. So obviously this one returns a tuple. It returns a tuple. If the integer has a perfect square, it returns true and its square root. But if it does not have a perfect square, what is it returned? It returns false because i squared will not be n. So let us take the example of 5. Let us say n is 5. Start at i is equal to 1. i star i is 1. 1 is less than 5. So it will increment. You go to 2. 2 star 2 is 4, less than 5. So it will go to 3. 3 star 3 is bigger than n. It will get out of the while loop. It will return i star i, which is 3 star 3, 9 is not equal to 5. So it will return false comma 3. So it returns the number closest to the square root if it is not a perfect square. So you have to read again. Small functions can also cause you. You have to think about them and read them carefully before you understand what they do. So let us move on. This should hopefully have given you some idea of an exercise of how to read code and also shows you the value of writing dark strings. If somebody has written perfect square, is perfect square? You would have known, oh it has something to do with perfect square. And then given a dark string saying, oh this returns first thing this, second thing this. Here is an example of how to use it. Someone had been nice enough to document it like that. You would be very happy. So when you write your functions, it is a good idea to write dark strings. Okay. So the next thing we need to learn is how do we create functions that take default arguments? Now we have seen the use of default arguments many times during this course. Early in the morning we looked at round. Round took one optional argument, the number of digits to round two. Similarly, split took a couple of optional arguments. One was the quantity to split on. The second was the maximum number of splits that are allowed. Similarly range, all of these took default arguments. So how do we write our own functions that take default arguments? Well it is very easy. Consider this function. Okay, some of you are getting problems running that little piece of code. Well, let us see. Let us answer that question. So if you did not get the right answer, you did not type it correctly. Okay. So let us move on. How do we create default arguments? So look at this example. Def, welcome. So welcome is the name of the function. Greet, name is equal to world. Notice that the second argument I say name is equal to world. So this is a default argument. Now I say print greet comma name. So let us try this. So how do I use this? I can say welcome. Now if I call it with just high, it will print high world. Okay. On the other hand, if I say high, it will say high Guido. Okay. But what if I did not give anything? This should be an error. And this will be an error. It will say welcome takes at least one argument zero given. Okay. So this is how you use default arguments. It gives us hello world. Okay. Now note that you cannot give default arguments after what are called positional arguments. Default arguments should always be placed at the end. You cannot do this. Why? Because if I do give one argument, it is not clear to python whether you are calling greet or are you setting the value of name. So optional arguments are always specified last. Very, very important for you to know. If not, you will get very funny errors. Let us try it and see what error we get. It is a syntax error. Non-default argument following default argument. Okay. So remember this is the way you do default arguments. Now python in addition to default arguments has something called keyword arguments. These are extremely powerful. So what you do is the same function we had in the previous slide welcome greet name equals world print greet comma name. If I do welcome hello James, it will say hello James. But if I say welcome hi name equals Guido. Notice that I am saying name equals Guido. It will give me hi Guido. Correct? But now if I say welcome name equals Guido, give that first and then say greet equals hey, what will it do? It will print hey Guido. So let us try this. Welcome. Now the reason it works is because I have explicitly said greet is this, name is this. So python has a very clear way of knowing what you are meaning. There is no ambiguity here. But if I did pressify this, let us say I did this. This will be a syntax error again because non keyword arguments you specified after any keyword arguments. So the rule is you always specify your positional or non keyword arguments first and then your keyword arguments. And this also makes it convenient when you have lot of arguments to pass. You have several arguments. You can always change one of them by saying name equals this. You do not have to say x supposing you have def, f, x equals 1, y equals 2, z equals 3. And now if I want to change the value of z, I do not have to say f of 1 2 3. I can say f of z equals that will work because everything is a keyword argument in this particular example because everything has a name. So anything that has a name equals value is called a keyword argument. It also serves as a default argument. So if you do not specify a keyword argument as in this example that I have shown you on this session, they are all defaulted to the default arguments that you have supplied. So essentially defining functions is extremely easy in Python. It supports default arguments. You can return any number of outputs, return no outputs. You can take any number of arguments and you can have default and keyword arguments. In addition there are a variety of built-in functions apart from the ones you create like a, b, s we have seen before. Any, you have not seen, all, len, max, min, power, range, sum, type. Any of these you have seen before except all, except any and all, all of the rest we have seen before. In addition there are several built-in functions that are available that you should look at. Visit this website docs.python.org slash libraries slash functions. It will tell you all of the built-in functions that are available with the Python interpreter. Now when you start writing functions there will be several doubts you will have. It is important we answer several of these right away. The first thing is that arguments that you pass are completely local. Which means if you take this case, def change q, q is equal to 10, print q. What will happen here? So let us type this out and let me explain to you. Further let me define a q is equal to 10 here. Now let us say change q. What will happen? Actually let me make q is equal to 1 here. Change q. Is this clear? This function is simply setting a local variable here q equals 10, print q. Now I am calling change of q. What happens? If you look at this q is the print value in the function is 10. What is the value of q here? So let me go through this again. What happens when I do change of q is q is sent here. The value of q is 10, but it is the value that sent. A number, numbers, strings and tuples in Python are immutable. Anything that is immutable when it is passed to a function is passed as a copy. So if I pass q here, the value 10 is passed. q on the other hand is set to 10. Actually value 1 is passed, but q internally is set to 10. This q is a local variable that shadows this big q here. And if I print this q it will only print the local variable here. Therefore it prints 10 and the value here locally which is 1 is unaffected. So let me give you another function just to illustrate my point. Now in this case I am creating, I am talking about x dot append means x should be a list. This x by the way has nothing to do with the x that is defined in the function. So let me say, let me not call it x. Doesn't matter. Let me call it x. Now if I say f of x, the value that it will print there is 1 because it has gotten a list. It has not made a copy of that list because if I say print x here, that will also have 1. So when I pass, now list is a mutable quantity. So if I pass the list there, I change that list. That list has changed there and also changed here. However, I am now going to modify this function to do the following. And now the local variable here x is unchanged. So the name x that I use here in the function definition, this name x has nothing to do with the name that is here. When I pass this f of x, a new x is created here as in a new name. This x here has nothing to do with x in this function definition here and here is not the same x as x is here. So when I am saying the f of x over there, that is not actually the same as this x, the variable. So if I set x is equal to 10 here, the quantity x that is defined here is completely local to this function. So anything that has been passed here does not matter. It is irrelevant because this is going to be creating a new local variable called x whose value is 10. The variable that is outside the scope of this function is still the same old list and that is also called x and both of them can coexist happily. There is no problem with respect to that. Essentially any arguments you pass to a function are entirely local. However if the value of those arguments, if they are mutable quantities like a list or later you will see dictionaries or a function or whatever you pass, these are all modifiable in the function. As the previous example, if you pass a list to a function and the function adds or modifies one of the elements of the list, the fellow who called that function will also be aware of that change. But if I have a variable inside the function, that variable has nothing really to do with a variable outside the scope of the function. As in this example that I showed you on the shell, this x is equal to 10 is completely local to this function definition and has nothing to do with the x that is outside in the global namespace. So essentially one important thing you will have to understand in Python is everything in Python is an object. Everything in Python you see is an object and every variable you see is simply a name that is bound to that object. As a simple example, I am going to say a function equals f. What is f? f is the function. So I can say function of 12. So which means a function is also an object. I can assign a variable to it just like string is an object, list is an object, integer is an object. Same way functions are also objects. So I can pass one function to another function, no problem. Supposing I have the following. I have a is equal to 1, 2, 3. What I am doing in this assignment statement is I am setting the name a is bound, is connected to this object that is a list containing 1, 2, 3. So now if I say b is equal to a, no copy is made. b is simply a new name for 1, 2, 3. Is that clear? It is like inside this class you will address me as sir or something. Outside somebody, a friend of mine will address me as Prabhu. Just because he addresses me as Prabhu, I do not change. I am still the same person. You addressing me as sir does not change me. I am still the same. Same way this list object here 1, 2, 3 is the same. When I say a equals this, it has bound this name a to that object 1, 2, 3, that list 1, 2, 3. When I say b is equal to a, it has a new name that is all. Someone asks me how do you delete an object? If all the reamed references to an object are gone, that object is automatically garbage collected by the Python virtual machine. How do you remove that name to object binding? You use the keyword del. Now if I type b, it will say b is not defined. But a is still there and I can still say b is equal to a. Now the name b is available. Now if I del a what happens? b will not go away. b is still there because the object that it is pointing to is still there. So essentially when I say a equals 1, 2, 3, the name a in the current interpreter session is associated with that 1, 2, 3 object. When I say b is equal to a, b is connected to the same object that a is connected to. Now if I say del a, that a is gone, that name a is gone but b name is still there and b is still at 1, 2, 3. But now if I del b, there are no longer any references to this 1, 2, 3 list and the garbage collector which sometime we say no one is using this and destroy that object. So you do not have to worry about it. So if I pass a mutable quantity to a function, another name is bound to this function to this object. So when I say def change q or let me retype that function. If I say x is 1, 2, 3, the name obj when I do change of x, x is the name for this list obj will become another name for this list and obj will be appended because they are referring to the same object both x and obj will change. However if I did def change and if I accidentally did obj equals 1, x is still intact because x is simply a name bound to this 1, 2, 3 object and obj is just a temporary variable created inside this function that has been set to 1 outside the scope of this function that obj is meaningless. You cannot get back obj unless you returned it or something like that. So essentially any arguments passed to a function are local. Names are bound to objects. If you pass a mutable quantity into a function the mutable quantity is passed and you can make changes to that mutable quantity in that function. If it is immutable you cannot change. So it is almost like a copy is being made. Any new variables created in that function's scope are local to that function. You cannot access them outside. So here is another example n is equal to 5. So let us try this. This is an interesting example. n is equal to 5 def change. If I do print n here what will happen? Should it be an error? You would think it should be an error but actually it would not be because if this function will look inside its scope to see if there is any n, it does not find anything. It will go one level higher and try to find a name outside the scope. If it finds it in the global name space it will use that. So this is so if for example if I change n to 100, change will see 100 because essentially at the level where you are calling the function there are names bound to objects. That function is inside this. So that function will look locally to see if there are any new names. The n is not there. It will go outside one level and ask is there any n there and return it. However if I wrote the code as follows, now it will only print n and my n value is not changed because any variables created inside the function as described in the slide are local. Just like in the previous example I have said. Now if you want to assign to a global variable you have to use the global keyword. You have to say global n and then you can assign. In this case this will change the global quantity. Now here is another example of what I was talking about names, about mutable quantities. So what happens here is name is Mr. Steve Gosling. If I say def change name, name 0 is doctor. It will actually change Mr. to doctor. Similarly if I say n is 5, change n, n is 10. This n will be 10. But the change outside this n will be 5 because this creates a local variable and over here it is actually changing this because name is not defined anywhere inside the function. It will look on the outside. So let me recap functions. Defining a function is simple. The structure is define function name def function name arguments colon block. You can either return single nothing or multiple arguments. In this case is an example of returning nothing. In this case returning one, taking two arguments, adding a doc string to a function is very useful and the ipython session can give you the documentation immediately. You can return multiple arguments, multiple values from a function by simply returning them as something, comma, something, comma, something. As many as you want you can return. The other thing to note is it does not matter sometimes you can return one, sometimes you can return two. That also can be done. So for example, let me write a little function here. So in this case if I say f of 1, f of 1 it returns odd. If I return f of say 12, it returns two quantities. So just because you return two in one place does not mean you should only return two. There is nothing like that. You can return whatever you want. Two, one, nothing up to you. To get back arguments that are return multiple values, you have to accept them as multiple values. Essentially if you return multiple values it returns a tuple. We look at tuples right after this functions are done. We looked at two code reading examples so that you get some familiarity with reading python code. Default arguments can be easily provided for python functions. We have seen many examples like a thrown, split, range, etc. The way to do it is to define the same functions and give it to special, give it an argument, give the argument a specific name or rather in this example welcome greet and give the argument past a specific value by saying name equals something. These equals can be one or two, can be whatever you want and when you call it only the greet has to be supplied compulsorily. If you do not supply greet name will be assumed to be word. Otherwise you can explicitly supply greet is equal to something, name is equal to something which is called keyword arguments. Please note you cannot supply default arguments first and then optional arguments, non keyword arguments afterwards. This is syntactically incorrect. Keyword arguments is the same idea. The idea being instead of specifying quantities based on their position in the function you explicitly give their names. So in this case I can say welcome greet equals hey, name equals word and now I can put the order in any order I want because python explicitly knows name is this, greet is this. So I do not, there is no ambiguity with respect to this position has to be greet, that position has to be name is not there because I have explicitly given the names and that is the essence of keyword and default arguments. There are variety of built in functions that you can use. Important thing when you create functions is that arguments given to a function are completely local which means in this case this q is local to this function definition. It is not available outside the scope of this function. In this case if I say n is 5 outside and say n is 10 inside, this n will always remain 10 and the n outside the global scope will be 5. But if I did not define this line, one line was blank imagine or commented out, print n will print 5. However if you want to set the value of n 5 outside you must use the global keyword. If a quantity is mutable, if it is accessible to the function it will be changed. You can change it rather. So if I give you a list in a function, the function is fully capable of changing that list and modifying the list. The final point which is not there in these slides is that you have to remember this notion of names being bound to objects. These are called namespaces in Python. A namespace is simply this mapping between names like x is equal to 1. But x is a name that value 1 is what this name x is mapped to that is the object. So basically in Python you create a variety of objects and you associate names to these objects. That is how the variables behave in Python. Once you understand this concept many of the common mistakes that you may make or common misconceptions will kind of disappear because it is very easy to understand from this perspective that a variable is nothing but a name that is bound to an object. So for example if I take a is 1, 2, 3 and I say b equals a, if I do del, if I do a dot append b is also changed. But if I do del of a, b is still the same again. So the del keyword destroys this name binding. a is connected to this 1, 2, 3, 1 object. b is also connected to the same. There is another name for it. When I say del a that name is removed. That is one point that many people often find very nice and interesting is the fact that functions in Python are what are called first class objects. So if I say def, f, x, return x plus 1 or let me say def average. So let us say I have a function. I will finish with this final point. So supposing I want to write some function that will integrate and contriving too much of an example. So supposing I want to create a function that accepts another function and calls that function inside. So let us say I create def g given a function f. It will call that function f. Now if I say g of f, it returns or it prints 101. But now if I take, let us define another function h and now I say g of h. So notice that I can pass a function just like I would pass any variable in this programming language. This makes it very powerful to write code because a function is just like any other variable and I can bind names to these functions. So if you are writing for example an integrator and you want to do trapezoid integration, you write a function just like anything else. You write the function and you want to integrate some other function. You pass that as a function. It will calculate it and calculate the area under that curve. So the nice thing about Python is these functions that we have defined so far, they are what are called first class objects. Which means I can treat them like any other variable. So I can assign variables to them. I can pass them to functions. I can put them in a list. So for example I can say functions equals f, g and h. Then I can say for func in functions actually only f and g does not call this. For f or func in functions func of 100. So these kind of things become very easy to do. So now because functions just like anything else I can use functions. So this allows for very convenient programming things that you can do. So we will stop here for today. Tomorrow we will look at tuples, dictionaries, sets. Probably we will finish that in about 20 slides. We will finish that by one and a half sessions and then we will move on to advanced topics. We will look at maybe giving you some exercises.