When we last talked about parameters, it was just the basics: for each value you want your function to have, you have one parameter. Python supports a lot of options when it comes to parameters though. You can:
We'll start with the most basic of these topics: default values. The greet_user2
function from earlier allowed the user to pass in a greeting and a name. If we want, we could provide a default greeting. To do that, in the parameter list, after the parameter's name, we assign the default value. In the example below, we provide a default value for greeting
.
Code | Output |
---|---|
print(greeting, ", ", name, "!", sep="") greet_user3("Michael", 'Hi') greet_user3('Michael') |
Hello, Michael! |
Since greeting
has a default value, if the user doesn't provide an argument for greeting
, the default is used. However, if the user provides an argument, then that argument is used.
If you compare greet_user3
with greet_user2
, you'll notice that the order of the parameters is switched. That's because Python requires parameters with default values to come after parameters without default values. For example, this is the wrong form for a function header:
The default parameter is evaluated once, so you want to be careful not to modify it accidentally. Here's a simple example of this problem:
Code | Output |
---|---|
array.append(value) return array a1 = append_value(5) print('a1 = ', a1) a2 = append_value(1) print('a2 = ', a2) a3 = append_value(3) print('a3 = ', a3) a4 = ['a', 'b', 'c'] a5 = append_value('d', a4) print('a4 = ', a4) print('a5 = ', a5) a6 = append_value(6) print('a6 = ', a6) |
a2 = [5, 1] a3 = [5, 1, 3] a4 = ['a', 'b', 'c', 'd'] a5 = ['a', 'b', 'c', 'd'] a6 = [5, 1, 3, 6] |
Notice that when no list is passed into append_value
, it uses the default. The default starts out as an empty list, but each time something gets appended to it, the list holds onto that value it's still there the next time the function is called with no list passed in.
To fix this, use None
as a default value. Then, check whether array
is None
. If it is, assign into it a new list. For example:
Code | Output |
---|---|
if array == None: array = [] array.append(value) return array a1 = append_value2(5) print('a1 = ', a1) a2 = append_value2(1) print('a2 = ', a2) a3 = append_value2(3) print('a3 = ', a3) a4 = ['a', 'b', 'c'] a5 = append_value2('d', a4) print('a4 = ', a4) print('a5 = ', a5) a6 = append_value2(6) print('a6 = ', a6) |
a2 = [1] a3 = [3] a4 = ['a', 'b', 'c', 'd'] a5 = ['a', 'b', 'c', 'd'] a6 = [6] |
Both value
and array
are actually keyword arguments. You can specify values for keyword arguments in a unique way. When calling a method, instead of just specifying the argument, you can explicitly state that the parameter is getting a certain value. For example:
Why is this useful? In this case it's not. However, it can be useful if there are a lot of keyword parameters and you only want to give some of them non-default values. For example:
Code | Output |
---|---|
if convert_name == 'upper': name = name.upper() if convert_name == 'lower': name = name.lower() if convert_name == 'title': name = name.title() if convert_name in ('capitalize', 'cap'): name = name.capitalize() print(greeting, ", ", name, punct, sep="") greet_user4("Michael", 'Hi') greet_user4('Michael', punct='.') greet_user4('Michael', convert_name='cap', greeting='Hi') |
Hello, Michael. Hi, MICHAEL! |
You can also use default values to make some parameters optional. In the example below, if the average
function receives just one argument, then it assumes that it's a list or tuple and computes the average of the list/tuple. However, if it receives two values, then it assumes the first is the sum and the second is the number of values and computes the average from those values.
Code | Output |
---|---|
if count == None: if not isinstance(total, (list, tuple)): raise TypeError('If only one argument is given, it must be a list or tuple') return sum(total) / len(total) else: return total / count average((1,2,3,4)) average(4, 10) |
0.4 |
You can also have an unlimited number of positional arguments (actually, it's limited to how much your computer's memory can hold). This is called var-positional for "variable-positional" (variable in the sense that the number of arguments can vary). To accomplish this, you need to create a special parameter to hold all of these values. This special parameter must start with an asterisk (*
). You can have only one var-positional parameter.
Code | Output |
---|---|
if count == None: if not isinstance(total, (list, tuple)): raise TypeError('If only one argument is given, it must be a list or tuple') return sum(total) / len(total) elif len(values) == 0: return total / count else: return sum((total, count)+tuple(values)) / (2+len(values)) average2((1,2,3,4)) average2(4, 10) average2(4, 10, 8) |
0.4 7.333333333333333 |
Notice that if exactly two values are given, then *values
will be empty.
If you want to specify both keyword arguments and positional arguments, the keyword arguments must come after the positional ones. Same for the parameters. The example below will not work because average2 has count
come before *values
. So when average2
is called in the example below, count
will get two values, 2
and 8
.
To fix this problem, you must rearrange the parameter list to put *values
before the keyword parameters. This also requires some changes to the body of the function and in the way you call the function:
Code | Output |
---|---|
if isinstance(total, (list, tuple)): return sum(total) / len(total) elif count != None: return total / count else: return sum((total, )+tuple(values)) / (1+len(values)) average3((1,2,3,4)) average3(4, count=10) average3(4, 10, 8) |
0.4 7.333333333333333 |
Mixing keyword parameters and a var-positional parameters can get confusing. In practice, you don't often mix the two (although we've been using one that does since the beginning of the semester: print
.
Just like you can have a variable number of positional arguments, you can have a variable number of keyword arguments. In your parameter list, you create a special parameter to hold all of the specified keyword arguments. This special parameter must start with two asterisks (**
). You can have only one var-keyword parameter. This special parameter will then hold all of the keyword arguments (that don't belong to keyword parameters) in a dictionary, with the keys being the parameter names and the values being the arguments.
You might use var-keyword arguments if your function can take a lot of optional arguments but in most cases the user will only want to specify some of them. For example, if one parameter is used to specify how the function will behave and each behavior has different parameters that are needed (see the example below). Another is if there are a lot of parameters, but the user will likely only change the defaults for one or two of them.
Code | Output |
---|---|
''' Takes a list of numbers (values) and computes some statistics, then returns either a specially-formatted string (perhaps suitable for writing to a file), a tuple of values, or a dictionary. store is used to specify the storage (and return) type: 'dict', 'list', or 'str' (default is list). If 'str' is chosen, then the additional options are: * delim: the delimiter to use to separate values (default=',') * endl: the end-of-line string to use (default='\\n') * incl_head: include a header line with the label for each column (True or False, default=False) as_type is used to convert the elements in the values collection into the specified type (default is float) ''' if not isinstance(values, (list, tuple, set)): raise TypeError('values must be a list, tuple, or set') to_type = kwargs.get('as_type', float) values = [to_type(v) for v in values] retval = [] labels = [] length = len(values) retval.append(length) labels.append('length') total = sum(values) retval.append(total) labels.append('total') retval.append(float(total) / length) labels.append('mean') smallest = min(values) retval.append(smallest) labels.append('min') largest = max(values) retval.append(largest) labels.append('max') retval.append(largest - smallest) labels.append('range') store = kwargs.get('store', list) if store in (dict, 'dict'): return dict(zip(labels, retval)) elif store in (str, 'str'): delim = kwargs.get('delim', ',') end = kwargs.get('endl', '\\n') output = '' if kwargs.get('incl_head', False): output += delim.join(labels) + end output += delim.join([str(v) for v in retval]) + end return output else: return labels, retval array = [5.4, 3.5, 7.1, -2.4] results1 = calculate_stats(array) print('results1 =', results1) print() results2 = calculate_stats(array, store=dict) print('results2 =', results2) print() results3 = calculate_stats(array, store=dict, as_type=int) print('results3 =', results3) print() results4 = calculate_stats(array, store=str, incl_head=True) print('results4 =', results4) |
results2 = {'mean': 3.4, 'length': 4, 'range': 9.5, 'max': 7.1, 'min': -2.4, 'total': 13.6} results3 = {'mean': 3.25, 'length': 4, 'range': 9, 'max': 7, 'min': -2, 'total': 13} results4 = length,total,mean,min,max,range\n4,13.6,3.4,-2.4,7.1,9.5\n |
Notice that as_type
is actually taking a function and applying that function to each element in values
. You could actually pass in any function with one parameter. For example:
Code | Output |
---|---|
return val * val results5 = calculate_stats(array, as_type=squared) print('results5 =', results5) |
|
Lambda functions are also called anonymous functions. They're functions without a name. They're typically used for small, quick operations. The format for a lambda function is:
The parameter_list
is, of course, the parameter list for your function. The value_to_return
is, of course, the value your function will return.
Lambda functions differ from regular functions in a couple of ways:
For example, to rewrite the squared
function from above:
It can then be used the same way as above:
How is this any better than a regular function? Its usefulness comes in when you just need a quick function for a brief time, like in our example above. Technically, we could just do:
Variables created in a function exist until that function is over, then they're destroyed. Variables created in a function only exist within that function. However, variables created outside of a function might be accessible inside the function. Variables created outside of functions (and classes) are called global variables. In the example below, val
is a global variable.
Code | Output |
---|---|
def print_val(): print(val) print_val() |
|
However, this only works for variables you are getting the value of. If you try to set the global variable from within a function, that will not change the global variable:
Code | Output |
---|---|
def change_val1(): val = 10 print('val before =', val) change_val1() print('val after =', val) |
val after = 5 |
If you try mixing getting a global variable's value and changing its value, this will also create a problem:
Code | Output |
---|---|
def change_val2(): print('val inside =', val) val = 10 print('val before =', val) change_val2() print('val after =', val) |
Traceback (most recent call last): File <stdin>, line 7, in <module> change_val2() File <stdin>, line 3, in change_val2 print('val inside =', val) UnboundLocalError: local variable 'val' referenced before assignment |
If you would like to change a global variable within a function, you must tell the function that the variable is a global variable using the global
keyword:
Code | Output |
---|---|
def change_val3(): global val print('val inside =', val) val = 10 print('val before =', val) change_val3() print('val after =', val) |
val inside = 5 val after = 10 |
You can also create a global variable from within a function:
Code | Output |
---|---|
global data_dir data_dir = input("What folder is the data stored in? ") get_data_directory() print(data_dir) |
'C:\Users\mil28\Documents\' |
It is considered bad programming practice to modify global variables from within a function. Since functions are generally self-contained, the user would not expect variables outside of the function to change when a function is called. However, global constants are considered ok. This is why you can easily access global variables inside a function, as long as you don't change them.
<< Previous Notes | Daily Schedule |