From 63df73d699495324dbd69fb1b3b22fca2b3056d6 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Tue, 6 Feb 2018 13:25:53 +0000 Subject: [PATCH] Finished args/kwargs practical. --- .../function_inputs_and_outputs.ipynb | 129 +++++++++++++++++- .../function_inputs_and_outputs.md | 101 +++++++++++++- 2 files changed, 222 insertions(+), 8 deletions(-) diff --git a/advanced_topics/function_inputs_and_outputs.ipynb b/advanced_topics/function_inputs_and_outputs.ipynb index 2306f9e..d18fc09 100644 --- a/advanced_topics/function_inputs_and_outputs.ipynb +++ b/advanced_topics/function_inputs_and_outputs.ipynb @@ -72,7 +72,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You can think of the star operator as 'unpacking' the contents of the\n", + "You can think of the star operator as _unpacking_ the contents of the\n", "sequence.\n", "\n", "\n", @@ -98,7 +98,7 @@ "metadata": {}, "source": [ "Python has another operator - the double-star (`**`), which will unpack\n", - "keyword arguments from `dict`. For example:" + "keyword arguments from a `dict`. For example:" ] }, { @@ -149,7 +149,7 @@ "metadata": {}, "outputs": [], "source": [ - "myfunc(a=1, b=2, c=3):\n", + "def myfunc(a=1, b=2, c=3):\n", " print('First argument: ', a)\n", " print('Second argument:', b)\n", " print('Third argument: ', c)" @@ -239,7 +239,128 @@ "defined, and will persist for the duration of your program. So in this\n", "example, the default value for `a`, a Python `list`, gets created when\n", "`badfunc` is defined, and hangs around for the lifetime of the `badfunc`\n", - "function!" + "function!\n", + "\n", + "\n", + "## Variable numbers of arguments - `args` and `kwargs`\n", + "\n", + "\n", + "The `*` and `**` operators can also be used in function definitions - this\n", + "indicates that a function may accept a variable number of arguments.\n", + "\n", + "\n", + "Let's redefine `myfunc` to accept any number of positional arguments - here,\n", + "all positional arguments will be passed into `myfunc` as a tuple called\n", + "`args`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def myfunc(*args):\n", + " print('myfunc({})'.format(args))\n", + " print(' Number of arguments: {}'.format(len(args)))\n", + " for i, arg in enumerate(args):\n", + " print(' Argument {:2d}: {}'.format(i, arg))\n", + "\n", + "myfunc()\n", + "myfunc(1)\n", + "myfunc(1, 2, 3)\n", + "myfunc(1, 'a', [3, 4])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, we can define a function to accept any number of keyword\n", + "arguments. In this case, the keyword arguments will be packed into a `dict`\n", + "called `kwargs`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def myfunc(**kwargs):\n", + " print('myfunc({})'.format(kwargs))\n", + " for k, v in kwargs.items():\n", + " print(' Argument {} = {}'.format(k, v))\n", + "\n", + "myfunc()\n", + "myfunc(a=1, b=2)\n", + "myfunc(a='abc', foo=123)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a useful technique in many circumstances. For example, if you are\n", + "writing a function which calls another function that takes many arguments, you\n", + "can use ``**kwargs`` to pass-through arguments to the second function. As an\n", + "example, let's say we have functions `flirt` and `fnirt`, which respectively\n", + "perform linear and non-linear registration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def flirt(infile,\n", + " ref,\n", + " outfile=None,\n", + " init=None,\n", + " omat=None,\n", + " dof=12):\n", + " # TODO get MJ to fill this bit in\n", + " pass\n", + "\n", + "def fnirt(infile,\n", + " ref,\n", + " outfile=None,\n", + " aff=None,\n", + " interp='nn',\n", + " refmask=None,\n", + " minmet='lg',\n", + " subsamp=4):\n", + " # TODO get Jesper to fill this bit in\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We want to write our own registration function which uses the `flirt` and\n", + "`fnirt` functions, while also allowing the `fnirt` parameters to be\n", + "customised. We can use `**kwargs` to do this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def do_nonlinear_reg(infile, ref, outfile, **kwargs):\n", + " \"\"\"Aligns infile to ref using non-linear registration. All keyword\n", + " arguments are passed through to the fnirt function.\n", + " \"\"\"\n", + "\n", + " affmat = '/tmp/aff.mat'\n", + "\n", + " # calculate a rough initial linear alignemnt\n", + " flirt(infile, ref, omat=affmat)\n", + "\n", + " fnirt(infile, ref, outfile, aff=affmat, **kwargs)" ] } ], diff --git a/advanced_topics/function_inputs_and_outputs.md b/advanced_topics/function_inputs_and_outputs.md index 5777da1..234cea4 100644 --- a/advanced_topics/function_inputs_and_outputs.md +++ b/advanced_topics/function_inputs_and_outputs.md @@ -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) +``` -- GitLab