[ Team LiB ] |
12.4 A Second Example: Intersecting SequencesLet's look at a second function example that does something a bit more useful than multiplying arguments, and further illustrates function basics. In Chapter 10, we saw a for loop that collected items in common in two strings. We noted there that the code wasn't as useful as it could be because it was set up to work only on specific variables and could not be rerun later. Of course, you could cut and paste the code to each place it needs to be run, but this solution is neither good nor general—you'd still have to edit each copy to support different sequence names, and changing the algorithm then requires changing multiple copies. 12.4.1 DefinitionBy now, you can probably guess that the solution to this dilemma is to package the for loop inside a function. Functions offer a number of advantages over simple top-level code:
In effect, wrapping the code in a function makes it a general intersection utility: def intersect(seq1, seq2): res = [ ] # Start empty. for x in seq1: # Scan seq1. if x in seq2: # Common item? res.append(x) # Add to end. return res The transformation from the simple code of Chapter 10 to this function is straightforward; we've just nested the original logic under a def header and made the objects on which it operates passed-in parameter names. Since this function computes a result, we've also added a return statement to send a result object back to the caller. 12.4.2 CallsBefore you can call the function, you have to make the function. Run its def statement by typing it interactively, or by coding it in a module file and importing the file. Once you've run the def one way or another, you call the function by passing any two sequence objects in parenthesis: >>> s1 = "SPAM" >>> s2 = "SCAM" >>> intersect(s1, s2) # Strings ['S', 'A', 'M'] Here, the code passes in two strings, and gets back a list containing the characters in common. The algorithm the function uses is simple: "for every item in the first argument, if that item is also in the second argument, append the item to the result." It's a little shorter to say that in Python than in English, but it works out the same. 12.4.3 Polymorphism RevisitedLike all functions in Python, intersect is polymorphic—it works on arbitrary types, as long as they support the expected object interface: >>> x = intersect([1, 2, 3], (1, 4)) # Mixed types >>> x # Saved result object [1] This time, we pass in different types of objects to our function—a list and a tuple (mixed types)—and it still picks out the common items. Since you don't have to specify the types of arguments ahead of time, the intersect function happily iterates through any kind of sequence objects you send it, as long as they support the expected interfaces. For intersect, this means that the first argument has to support the for loop, and the second has to support the in membership test—any two such objects will work. If you pass in objects that do not support these interfaces (e.g., passing in numbers), Python will automatically detect the mismatch and raise an exception for you—exactly what you want, and the best you could do on your own if you coded explicit type tests. The intersect function will even work on class-based objects you code, which you will learn how to build in Part VI.[1]
12.4.4 Local VariablesThe variable res inside intersect is what in Python is called a local variable— a name that is only visible to code inside the function def, and only exists while the function runs. In fact, because all names assigned in any way inside a function are classified as local variables by default, nearly all the names in intersect are local variables:
All these local variables appear when the function is called, and disappear when the function exits—the return statement at the end of intersect sends back the result object, but name res goes away. To fully understand the notion of locals, though, we need to move on to Chapter 13. |
[ Team LiB ] |