So there's actually two different ways to pass function arguments. The way we've been illustrating so far is to pass them by position. So here I show you a small sequence, and I call the has_stop_codon function with has_stop_codon function giving it the argument seq, for our DNA sequence, and the frame one. And it knows because the arguments are in that order, that as I give the sequence first and the reading frame second, it knows what to do with them. You don't really have to do that if you know the variables that are defined in the function, you could actually pass them in by name. So I could has has_stop_codon(frame=0,dna=seq). Instead of doing, now reverse the order of the arguments. That's only allowed if you know what those variable names are in the function definition, since the function itself uses variables called frame and called dna. I can specifically assign them that values when I call the function, if I know what those variables are. So here that function call works and returns true. And the order of the arguments is does not have to be the same. You can mix either style but you always have to put the named arguments at the end. In general though I'd recommend not using this second style because you have to know something about the internal definition of the function, which usually you don't want to worry about. Let's talk about now a more complex function, reverse compliment, so DNA sequences, as you should know, DNA is double stranded. Every chromosome in your body has a double strand of DNA sequence and the two strands are complimentary to one another. So one completely defines the other. A's are always lined up with T's, and G's are always lined up with C's. So once you know one sequence you know the other, but they go in opposite directions, too, because DNA has directionality to it. So it's not uncommon for us to want to take the reverse complement of a DNA sequence. Frequently when we're looking for a pattern in DNA we don't know which strand it occurs on, so we want to look at both strands. So if I provide one strand of the DNA or one sequence to my program, I might want to generate the reverse complement and look at that too. So that's a very useful function we use a lot with doing genomics analysis. So let's define a function that'll do that on a DNA string. So we're going to call this function reverse complement, and it's going to return the reverse complement of a sequence that we provide. It's going to be a copy of that sequence. So, we can do it like this. We'll go into details about how this function works in the next few slides. So we'll say sequence equals or seq = reverse_string(seq), and then we'll say seq = complement(sec). So first we're going to reverse it, and then we're going to take its complement, and then we just return that. So that's a function but it needs two subsidiary functions, reverse string and complement to work. And then we return the sequence that we need, that we've now reverse complemented. So here's an example of what we'd like it to return, we call it reverse complement on a DNA sequence. It starts with CCGGAA, but it ends with CTTAG. The string that returns starts with CTAAG, which as you noticed is the reverse complement starting in the other direction of the string we've provided. So now let's go into a little more detail of how we make this work. So first, reversing a string actually is very easy in Python. Remember that slices, you can take slices of a string just by specifying where they begin and where they end using an index. S here are sequences called DNA again and we give a series of A's, C's, G's, and T's. And if we set DNA from 0 to 3, that would give us positions zero, one, and two, remember we don't include the last index position in the return value. So that would give us AGT or the first three bases in our string. There's extended syntax for this so we could take slices in a different way. If I take a slice by saying dna from zero to three, 0:3:2, that means go from zero to three but skipping along every two positions. So my increments going to be two positions through the string instead of one. So now I'm just going to get A and T. That is the first. The zeroth position, and the second index, which is the letter T. So I'm skipping along the string instead of every position, I've decided to increment it by two instead of by one, which is the default. So, that step argument allows us to do the following, very nice little trick. So I can say if I provide a colon with no arguments on either side that gives me the whole string. So I can say dna[:] and that will give me the whole string. So if I do that same trick but now I add this step argument. And I say -1 that says, oh, well my argument's going to be. I'm going to take the whole string as my target, and my step is going to be -1, so Python knows that means you want to go backwards through the string. So this will give me the entire string, but incrementing from the end, going backwards one base at a time. In other words, reversing the string. So if I wanted to find a function, reverse string, the one that I used on the previous slide, all I have to do is call the, use this special slice syntax. So here is an example of that function def reverse_string, takes an argument called seq and all I do is return seq[::-1] as the slice, and that's the reverse of seek. So here I'm showing how it worked. We call reverse string on DNA sequence and it returns us the reverse of that. So that's the first of the two functions that we need, the sub-functions that we need in our reverse compliment function. The other is a complimenting function. So let's define that next. So, compliment DNA is going to take a sequence and return the compliment. So remember that the base pairing rules, the complement is the complementary base. So the base pairing rules in DNA are that A and T bind together, and C and G bind together. So let's make a little dictionary called basecomplement. So remember how we defined dictionaries, we use the curly brackets, and we have keys and values. So the capital A will correspond to T, capital C we'll have correspond to G, and then we have to do the reverse G corresponds to C. T corresponds to A, and we'll do the same what we'll define entries for all the lowercase letters as well as the uppercase letters. And we'll do it for N as well in case there are N's that are in our sequence that are either upper or lowercase N's. So now what we want to do now that we got our dictionary, we want to do a couple of slightly more complicated things that I'll explain in a little more detail. We'll create a new sequence called letters, and we'll call the built-in function list on DNA. I'll explain that next. And then we're going to take the complementary base for each of those letters and return a value, which is going to be a reverse complement. Let's go through these in a little bit more detail. So first let's look at this list function that I called. So, list comprehensions in Python provide a concise way to create lists. So, this built in function's what I just used. So, a common application of list comprehensions are to make new lists where each element of the list is a result of some operation applied to each member of another sequence. Or to create a subsequence of those elements that satisfy condition. So the syntax is going to be something like this. A new list is going to be equal to some operation I'm going to call, with an argument i. Where i comes from my old list, with perhaps some tests on it. So that's the sort of general syntax for i in old list. If filter on i. So operation and filter are things that we're going to define. So that's equivalent to setting up a new list, starting it off with nothing in it, and then going through my old list one item at a time, doing some kind of test or filter on each item. And then if it passes that filter, we're going to add something to our new list, so we're building our new list, and we're going to add that thing by doing some operation on the old list. In the case that we're describing, we're going to just take the reverse complement of each of the bases. So that's going to be our operation. So let's see how we do this in a DNA sequence. So our DNA sequence will be this string here, starting with A, G, T, G, T, G, so on. Now it will define our dictionary as before. Now let's call that list function that I showed before. So I'm going to say letters equals list of DNA. So what does that do? It creates a list whose items are the same as our original string, and in the same order. But now it's a list instead of a string. So what we get back where we just type letters to the Python Interpreter, we'll see that it's A, G, T, G, T, and so on in a list. So each of those is now a separate item in our list and now we can go through that list and reverse complement each one. So for that I'm going to use my dictionary, and I'm going to do a list comprehension operation like I just explained. So I'm going to create a new value and assign that to letters again, by calling [basecomplement[base] for base and letters. So what does that syntax do? That says that for each base in the string in the list letters I'm going to apply the same operation, base complement of base, a dictionary look up, and I'm going to replace, I'm going to return what I get from that dictionary lookup, from that operation. So [basecomplement[base] is going to give me a G if I look up an A, it's going to give me a T if I look up, I'm sorry, it's going to give me a G if I look a C, a T if I look up an A, and so on. So I'm going to go through each of the items in my list, and letters, and I'm going to look it up in our dictionary. It's going to give us a reverse complement. And this syntax is going to return a new list, which has the reverse complement of each of the letters in the original list. So if I type letters again, I'll see that now each letter has been replaced with its reverse complement, sorry, with its complement. So, let's go back to our larger function on complement DNA. So, we've now finished complimenting all the bases, but we have a list we want to string. So we need to return them by, we need to return a string by joining them all together. So for that, we use this built in function called join. So I'm going to return this interesting syntax, where I put two quotes with nothing in between them .join(letters). So join is a method attached to any string. So I'm going to join these letters together so let me back up a little bit and talk about join in something complementary to that which is called split. So split and join are methods of the string object that are very useful. We use them all the time when we're doing DNA sequencing analysis. So split is a method that returns a list of all the words in a string. So I'm going to give an example, suppose our string, which were going to call sentence, is enzymes and other proteins come in many shapes. Then I call split, the split method by typing sentence.split(). That will return a list which has each of the words in my original sentence in the same order. So you see them here, enzymes and other proteins and so on, all split up into separate words. This is a kind of very useful function to do. That's known as splitting on white spaces. But we can split on other separators as well by specifying an optional argument. So if I say sentence.split and provide the string and it'll now split using the word and. So my original sentence was split into just two pieces divided by the word and. So you see now it returns. Enzymes is the first element of my new list, and other proteins come in many shapes is the second element. And the separator is not returned, so and goes away. So join is for the reverse of that. So join is going to return a string in which the string elements were joined from a list. So you give join a list of things, and it will give you back a string, where it's just jammed everything together from your list. And you have to give it a separator element because you can join things together and say put spaces in between them in your string. But in our case we don't want to do that. So here's an example where we're calling the join function using a dash as the connector so we can join back together a list which is the the elements, enzymes, and other proteins come in many shapes. And that will give us back a single string where all those words are connected by dashes because we gave join this argument dash which says that's what you use to join things together. So let's go back to our reverse complement function. We wrote return, and then we called join but we gave it the empty string, so quote quote the empty string. So it's going to join together all the letters with nothing separating them because we gave it the empty string. So now we're done. We type return, and then we give it the empty string to join together our letters, and now we've returned our complementary string of DNA. So now we finished our reverse complement function by defining reverse and complement, and our original function can now return the reverse compliment. So, finally you might want to have a variable number of arguments to your functions. So let's talk about how to do that. So, a typical function declaration would be, def myfunction and you would say the first, the second, and the third argument, and you would do something with those variables as we've been showing in the examples we've gone through so far. But it's possible to find function with a variable number of arguments using the following special syntax, you can define a function def newfunction and give it the arguments (first, second, third, *the rest). And the rest will be bound to assigned to all the rest of the arguments, you can give it one more argument or many more arguments. So this is a way to let Python accept a variable number of arguments to a function. So it's very simple. So if I called newfunction here with 1, 2, 3, 4, 5. With five arguments. And then what will happen is, first we'll be bound to the value 1, second we'll get 2, third we'll get 3, and all the rest will be a list of 4 and 5, which you can do something with. So that concludes our discussion of functions.