Skip to content
Snippets Groups Projects
Commit 63df73d6 authored by Paul McCarthy's avatar Paul McCarthy :mountain_bicyclist:
Browse files

Finished args/kwargs practical.

parent bd5b0e09
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
# Function inputs and outputs
In Python, arguments to a function can be specified in two different ways - by
using _positional_ arguments, or by using _keyword_ arguments.
## Positional arguments
Let's say we have a function that looks like this
%% Cell type:code id: tags:
```
def myfunc(a, b, c):
print('First argument: ', a)
print('Second argument:', b)
print('Third argument: ', c)
```
%% Cell type:markdown id: tags:
If we call this function like so:
%% Cell type:code id: tags:
```
myfunc(1, 2, 3)
```
%% Cell type:markdown id: tags:
The values `1`, `2` and `3` get assigned to arguments `a`, `b`, and `c`
respectively, based on the position in which they are passed.
Python allows us to pass positional arguments into a function from a sequence,
using the star (`*`) operator. So we could store our arguments in a list or
tuple, and then pass the list straight in:
%% Cell type:code id: tags:
```
args = [3, 4, 5]
myfunc(*args)
```
%% Cell type:markdown id: tags:
You can think of the star operator as 'unpacking' the contents of the
You can think of the star operator as _unpacking_ the contents of the
sequence.
## Keyword arguments
Using keyword arguments allows us to pass arguments to a function in any order
we like. We could just as easily call our `myfunc` function like so, and get
the same result that we did earlier when using positional arguments:
%% Cell type:code id: tags:
```
myfunc(c=3, b=2, a=1)
```
%% Cell type:markdown id: tags:
Python has another operator - the double-star (`**`), which will unpack
keyword arguments from `dict`. For example:
keyword arguments from a `dict`. For example:
%% Cell type:code id: tags:
```
kwargs = {'a' : 4, 'b' : 5, 'c' : 6}
myfunc(**kwargs)
```
%% Cell type:markdown id: tags:
## Combining positional and keyword arguments
In fact, we can use both of these techniques at once, like so:
%% Cell type:code id: tags:
```
args = (100, 200)
kwargs = {'c' : 300}
myfunc(*args, **kwargs)
```
%% Cell type:markdown id: tags:
## Default argument values
Function arguments can be given default values, like so:
%% Cell type:code id: tags:
```
myfunc(a=1, b=2, c=3):
def myfunc(a=1, b=2, c=3):
print('First argument: ', a)
print('Second argument:', b)
print('Third argument: ', c)
```
%% Cell type:markdown id: tags:
Now we can call `myfunc`, only passing the arguments that we need to. The
arguments which are unspecified in the function call will be assigned their
default value:
%% Cell type:code id: tags:
```
myfunc()
myfunc(10)
myfunc(10, b=30)
myfunc(c=300)
```
%% Cell type:markdown id: tags:
__WARNING:__ _Never_ define a function with a mutable default value, such as a
`list`, `dict` or other non-primitive type. Let's see what happens when we do:
%% Cell type:code id: tags:
```
def badfunc(a=[]):
a.append('end of sequence')
output = ', '.join([str(elem) for elem in a])
print(output)
```
%% Cell type:markdown id: tags:
With this function, all is well and good if we pass in our own value for `a`:
%% Cell type:code id: tags:
```
badfunc([1, 2, 3, 4])
badfunc([2, 4, 6])
```
%% Cell type:markdown id: tags:
But what happens when we let `badfunc` use the default value for `a`?
%% Cell type:code id: tags:
```
badfunc()
badfunc()
badfunc()
```
%% Cell type:markdown id: tags:
This happens because default argument values are created when the function is
defined, and will persist for the duration of your program. So in this
example, the default value for `a`, a Python `list`, gets created when
`badfunc` is defined, and hangs around for the lifetime of the `badfunc`
function!
## Variable numbers of arguments - `args` and `kwargs`
The `*` and `**` operators can also be used in function definitions - this
indicates that a function may accept a variable number of arguments.
Let's redefine `myfunc` to accept any number of positional arguments - here,
all positional arguments will be passed into `myfunc` as a tuple called
`args`:
%% Cell type:code id: tags:
```
def myfunc(*args):
print('myfunc({})'.format(args))
print(' Number of arguments: {}'.format(len(args)))
for i, arg in enumerate(args):
print(' Argument {:2d}: {}'.format(i, arg))
myfunc()
myfunc(1)
myfunc(1, 2, 3)
myfunc(1, 'a', [3, 4])
```
%% Cell type:markdown id: tags:
Similarly, we can define a function to accept any number of keyword
arguments. In this case, the keyword arguments will be packed into a `dict`
called `kwargs`:
%% Cell type:code id: tags:
```
def myfunc(**kwargs):
print('myfunc({})'.format(kwargs))
for k, v in kwargs.items():
print(' Argument {} = {}'.format(k, v))
myfunc()
myfunc(a=1, b=2)
myfunc(a='abc', foo=123)
```
%% Cell type:markdown id: tags:
This is a useful technique in many circumstances. For example, if you are
writing a function which calls another function that takes many arguments, you
can use ``**kwargs`` to pass-through arguments to the second function. As an
example, let's say we have functions `flirt` and `fnirt`, which respectively
perform linear and non-linear registration:
%% Cell type:code id: tags:
```
def flirt(infile,
ref,
outfile=None,
init=None,
omat=None,
dof=12):
# TODO get MJ to fill this bit in
pass
def fnirt(infile,
ref,
outfile=None,
aff=None,
interp='nn',
refmask=None,
minmet='lg',
subsamp=4):
# TODO get Jesper to fill this bit in
pass
```
%% Cell type:markdown id: tags:
We want to write our own registration function which uses the `flirt` and
`fnirt` functions, while also allowing the `fnirt` parameters to be
customised. We can use `**kwargs` to do this:
%% Cell type:code id: tags:
```
def do_nonlinear_reg(infile, ref, outfile, **kwargs):
"""Aligns infile to ref using non-linear registration. All keyword
arguments are passed through to the fnirt function.
"""
affmat = '/tmp/aff.mat'
# calculate a rough initial linear alignemnt
flirt(infile, ref, omat=affmat)
fnirt(infile, ref, outfile, aff=affmat, **kwargs)
```
......
......@@ -40,7 +40,7 @@ args = [3, 4, 5]
myfunc(*args)
```
You can think of the star operator as 'unpacking' the contents of the
You can think of the star operator as _unpacking_ the contents of the
sequence.
......@@ -58,7 +58,7 @@ myfunc(c=3, b=2, a=1)
Python has another operator - the double-star (`**`), which will unpack
keyword arguments from `dict`. For example:
keyword arguments from a `dict`. For example:
```
kwargs = {'a' : 4, 'b' : 5, 'c' : 6}
......@@ -86,7 +86,7 @@ Function arguments can be given default values, like so:
```
myfunc(a=1, b=2, c=3):
def myfunc(a=1, b=2, c=3):
print('First argument: ', a)
print('Second argument:', b)
print('Third argument: ', c)
......@@ -141,4 +141,97 @@ This happens because default argument values are created when the function is
defined, and will persist for the duration of your program. So in this
example, the default value for `a`, a Python `list`, gets created when
`badfunc` is defined, and hangs around for the lifetime of the `badfunc`
function!
\ No newline at end of file
function!
## Variable numbers of arguments - `args` and `kwargs`
The `*` and `**` operators can also be used in function definitions - this
indicates that a function may accept a variable number of arguments.
Let's redefine `myfunc` to accept any number of positional arguments - here,
all positional arguments will be passed into `myfunc` as a tuple called
`args`:
```
def myfunc(*args):
print('myfunc({})'.format(args))
print(' Number of arguments: {}'.format(len(args)))
for i, arg in enumerate(args):
print(' Argument {:2d}: {}'.format(i, arg))
myfunc()
myfunc(1)
myfunc(1, 2, 3)
myfunc(1, 'a', [3, 4])
```
Similarly, we can define a function to accept any number of keyword
arguments. In this case, the keyword arguments will be packed into a `dict`
called `kwargs`:
```
def myfunc(**kwargs):
print('myfunc({})'.format(kwargs))
for k, v in kwargs.items():
print(' Argument {} = {}'.format(k, v))
myfunc()
myfunc(a=1, b=2)
myfunc(a='abc', foo=123)
```
This is a useful technique in many circumstances. For example, if you are
writing a function which calls another function that takes many arguments, you
can use ``**kwargs`` to pass-through arguments to the second function. As an
example, let's say we have functions `flirt` and `fnirt`, which respectively
perform linear and non-linear registration:
```
def flirt(infile,
ref,
outfile=None,
init=None,
omat=None,
dof=12):
# TODO get MJ to fill this bit in
pass
def fnirt(infile,
ref,
outfile=None,
aff=None,
interp='nn',
refmask=None,
minmet='lg',
subsamp=4):
# TODO get Jesper to fill this bit in
pass
```
We want to write our own registration function which uses the `flirt` and
`fnirt` functions, while also allowing the `fnirt` parameters to be
customised. We can use `**kwargs` to do this:
```
def do_nonlinear_reg(infile, ref, outfile, **kwargs):
"""Aligns infile to ref using non-linear registration. All keyword
arguments are passed through to the fnirt function.
"""
affmat = '/tmp/aff.mat'
# calculate a rough initial linear alignemnt
flirt(infile, ref, omat=affmat)
fnirt(infile, ref, outfile, aff=affmat, **kwargs)
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment