## Python 3 Notes

[ HOME | LING 1330/2330 ]

## User-Defined Functions

### Functions: the Basics

Let's bust out some old-school algebra. You learned "functions" as something like:
f(x) = x2 + 1
In Python, defining the function works as follows. def is the keyword for defining a function. The function name is followed by parameter(s) in (). The colon : signals the start of the function body, which is marked by indentation. Inside the function body, the return statement determines the value to be returned. After function definition is complete, calling the function with an argument returns a value.
 ```>>> def f(x): return x**2 + 1 >>> f(4) 17 >>> ```
Let's try a different function. Below is a function that takes a verb (hopefully) and returns a gerund form:
 ```>>> def get_ing(wd): return wd + 'ing' >>> get_ing('walk') 'walking' >>> ```

### Multiple Parameters, Docstring

Below is a slightly more complex function. It takes two arguments, has a conditional in the function body, and starts with a string:
 ```>>> def same_initial(wd1, wd2): """Tests if two words start with the same character, and returns True/False. Case distinction is ignored.""" if wd1[0].lower() == wd2[0].lower(): return True else: return False >>> same_initial('apple', 'orange') False >>> same_initial('Annie', 'apple') True >>> ```
The """Tests if ...""" string is called a "docstring". Placed at the very top of the function body, it acts as a documentation on the function. This string gets printed out when you call help() on the function:
 ```>>> help(same_initial) Help on function same_initial in module __main__: same_initial(wd1, wd2) Tests if two words start with the same character, and returns True/False. Case distinction is ignored. >>> ```
Here's another example. This function returns a list of characters two strings have in common. (Would be better if there were no duplicates. Can you improve it?)
 ```>>> def in_both(wd1, wd2): "Takes two strings, returns a sorted list of common characters" common = [] for c in wd1: if c in wd2: common.append(c) return sorted(common) >>> in_both('pear', 'apple') ['a', 'e', 'p'] >>> in_both('linguistics', 'economics') ['c', 'i', 'i', 'i', 'n', 's', 's'] >>> ```

### Functions: Returning vs. Void

You might be thinking: "Wait a minute, I didn't see any return statement in the Defining Functions tutorial". You are right -- the get_legal() function Ed defined did not include any return statement, only a bunch of print functions. In Python, it is possible to compose a function without a return statement. Functions like this are called void, and they return None, Python's special object for "nothing". Here's an example of a void function:
 ```>>> def sayhello(who): print('Hello,', who + '!') print('What a lovely day.') >>> sayhello('Akbar') Hello, Akbar! What a lovely day. >>> ```
OK, then, how are void functions and "returning"-type functions different? Good question. Let me illustrate with examples. Here's get_ing() function defined above, and its void counterpart print_ing():
 ```>>> def get_ing(wd): return wd + 'ing' >>> def print_ing(wd): print(wd + 'ing') ```
Calling the two functions, you first notice the slight difference in the output. The returning function gives you a string (note ''), while the printing function shows the printed output of the string -- notice the absence of quotes. Note that the returned value shows up in the interactive shell environment only; in a script, only print commands result in an output display.
 ```>>> get_ing('interest') 'interesting' >>> print_ing('interest') interesting >>> ```
Now, since get_ing() returns a value, it can be scooped into a variable via an assignment statement. Try doing the same with print_ing(), and you run into unintended consequences: printing happens upon the assignment statement, and you end up with nothing as the variable's value, because this function, being void, returns nothing.
 ```>>> foo = get_ing('interest') >>> foo 'interesting' >>> faa = print_ing('interest') interesting >>> faa >>> ```
In addition, a value-returning function can be plugged right into another function. Again, since void functions return nothing, they can't. Below, get_ing('eat') is passed on to the len() function to a successful outcome. With len(print_ing('eat')), printing happens regardless and then it runs into a type error:
 ```>>> len(get_ing('eat')) 6 >>> len(print_ing('eat')) eating Traceback (most recent call last): File "", line 1, in len(print_ing('eat')) TypeError: object of type 'NoneType' has no len() ```
Lastly, if printing is what you need, it can also be done with the value-returning function: simply use the print statement with it.
 ```>>> print(get_ing('interest')) # prints return value interesting >>> print_ing('interest') interesting >>> ```
That took a while, but I hope you now clearly see the fundamental differences between the two function types. Truth be told, the returning-type functions are far more function-like and useful. In other languages, void-type functions are not even called a function at all -- they are called procedures instead.