|
797 | 797 | The behavior of such code could become arbitrarily hard to debug. |
798 | 798 |
|
799 | 799 | In cases where a parameter is of a mutable type and we require a default value for it, we could instead use \pythonilIdx{None}. |
800 | | -Inside the function body, we could then check for \pythonilIdx{None} and implement appropriate behavior. |
801 | | - |
802 | | -Back to business.\pythonIdx{function!parameter!by name}\pythonIdx{function!argument!by name} |
803 | | -What would we do if we want to specify the value of the parameter \pythonil{sigma} of our function, but leave \pythonil{mu} at its default value? |
| 800 | +Inside the function body, we could then check for \pythonilIdx{None} and implement appropriate behavior.% |
| 801 | +% |
| 802 | +\begin{sloppypar}% |
| 803 | +Let's circle back to passing arguments to functions. |
| 804 | +Assume that we have a function \pythonil{def g(x: int, y: int)}. |
| 805 | +Normally, we would call it like~\pythonil{g(1, 2)}, in which case the function body sees \pythonil{x == 1} and \pythonil{y == 2}. |
| 806 | +However, you can also pass in arguments very much in the same way that you use to assign a variable, in the form~\pythonil{parameterName=value}. |
| 807 | +We could write \pythonil{g(x = 1, y = 2)} or, if we feel naughty, \pythonil{g(y = 2, x = 1)}. |
| 808 | +Both variants will be equivalent to our original function call. |
| 809 | +All we did is to explicitly write the names of the parameters when providing their values.% |
| 810 | +\end{sloppypar}% |
| 811 | +% |
| 812 | +\pythonIdx{function!parameter!by name}\pythonIdx{function!argument!by name}% |
| 813 | +We now revisit our \pythonil{pdf} function defined in file \programUrl{functions:normal_pdf} and used in \programUrl{functions:use_normal_pdf}, given in \cref{lst:functions:normal_pdf,lst:functions:use_normal_pdf}, respectively. |
| 814 | +The function \pythonil{pdf} has a parameter \pythonil{x} without default value, followed by a parameter \pythonil{mu} with default value, which, in turn, is followed by a parameter \pythonil{sigma} with default value. |
| 815 | +What would we do if we want to specify the value of the parameter \pythonil{sigma} of our function, but leave \pythonil{mu} at its default value?% |
| 816 | +% |
| 817 | +\begin{sloppypar}% |
804 | 818 | We can do this by passing in values by parameter name: |
805 | 819 | \pythonil{pdf(-2.0, sigma=3.0)}~passes in \pythonil{-2.0} for~\pythonil{x} and \pythonil{3.0} for~\pythonil{sigma}. |
806 | | -It does not specify any value for~\pythonil{mu}, leaving it at its default value, which renders the call equivalent to~\pythonil{pdf(-2.0, 0.0, 3.0)}. |
| 820 | +It does not specify any value for~\pythonil{mu}, leaving it at its default value. |
| 821 | +This renders the call equivalent to~\pythonil{pdf(-2.0, 0.0, 3.0)}. |
807 | 822 | This passing in of arguments by specifying \pythonil{parameterName=value} also allows us to specify the arguments in arbitrary order. |
808 | 823 | \pythonil{pdf(mu=8.0, x=0.0, sigma=1.5)} is an example of this. |
809 | 824 | Don't do such things, though.% |
| 825 | +\end{sloppypar}% |
810 | 826 | % |
811 | 827 | \begin{sloppypar}% |
812 | | -\Cref{lst:functions:use_normal_pdf} provides also another interesting way to call a function in \python. |
| 828 | +\Cref{lst:functions:use_normal_pdf} illustrates also another interesting way to call a function in \python. |
813 | 829 | As we have established by now, the parameters of a function have names. |
814 | | -If we write something like \pythonil{mu=8.0, x=0.0, sigma=1.5} to assign arguments, this looks very similar to the way we created dictionary constants back in \cref{sec:dictionaries} and \cref{lst:dicts:dicts_1}. |
815 | | -Calling \pythonil{pdf(-2.0, sigma=3.0)} is equivalent to writing~\pythonil{pdf(x=-2.0, sigma=3.0)}.% |
| 830 | +We can write something like \pythonil{pdf(mu=8.0, x=0.0, sigma=1.5)} to assign arguments. |
| 831 | +Calling \pythonil{pdf(-2.0, sigma=3.0)} is equivalent to writing~\pythonil{pdf(x=-2.0, sigma=3.0)}. |
| 832 | +Passing arguments to a function basically means to a assign values to keys~(the parameters). |
| 833 | +This is at least a little bit similar to the creation of a dictionary \pglspl{literal}.% |
816 | 834 | \end{sloppypar}% |
817 | 835 | % |
| 836 | +In fact, \python\ allows us to also construct the arguments for a function call in a collection. |
818 | 837 | We can create a dictionary with the values \pythonil{\{"x": -2.0, "sigma": 3.0\}}. |
819 | | -Let's call this dictionary~\pythonil{args_dict}. |
820 | | -Can we now somehow pass in these values to~\pythonil{pdf}? |
821 | | -We indeed can: |
| 838 | +Let's store this dictionary in variable~\pythonil{args_dict}. |
| 839 | +Can we now somehow pass in the key-value pairs from \pythonil{args_dict} as parameter-argument pairs to~\pythonil{pdf}? |
| 840 | + |
| 841 | +Indeed, we can. |
822 | 842 | We just have to write~\pythonil{pdf(**args_dict)}.\pythonIdx{**!function parameter}\pythonIdx{function!parameter!**}\pythonIdx{function!argument!**}\pythonIdx{function!argument!keyword}\pythonIdx{dict} |
823 | 843 | Doing this will unpack the dictionary \pythonil{args_dict} and pass all the values under their assigned names in as arguments to their corresponding parameters. |
824 | 844 | \pythonil{pdf(**args_dict)} is thus equivalent to~\pythonil{pdf(x=-2.0, sigma=3.0)}. |
| 845 | + |
825 | 846 | Two things are to notice here: |
826 | | -First, the double~\pythonil{*}\pythonIdx{*!function parameter}\pythonIdx{function!argument!*}\pythonIdx{function!parameter!*} (called % |
| 847 | +First, the double~\pythonil{*}\pythonIdx{*!function parameter}\pythonIdx{function!argument!*}\pythonIdx{function!parameter!*}. |
| 848 | +The \pythonil{*} here is often called % |
827 | 849 | wildcard\pythonIdx{wildcard}\pythonIdx{function!argument!wildcard}\pythonIdx{function!parameter!wildcard}, % |
828 | | -star\pythonIdx{star}\pythonIdx{function!argument!star}\pythonIdx{function!parameter!star}, or % |
829 | | -asterisk\pythonIdx{asterisk}\pythonIdx{function!argument!asterisk}\pythonIdx{function!parameter!asterisk}) % |
830 | | -\emph{before} the dictionary, i.e., the~\pythonil{**}\pythonIdx{**!function parameter} is telling \python\ to unpack the dictionary this way. |
831 | | -Second, default argument values still apply here, i.e., \pythonil{mu} will have value~\pythonil{0.0} in this function call. |
832 | | - |
833 | | -Similarly, maybe we do not care about the parameter names but want pass them in by position, as we have always done in the past. |
| 850 | +star\pythonIdx{star}\pythonIdx{function!argument!star}\pythonIdx{function!parameter!star} or % |
| 851 | +asterisk\pythonIdx{asterisk}\pythonIdx{function!argument!asterisk}\pythonIdx{function!parameter!asterisk}. % |
| 852 | +The double-wildcard \pythonil{**} is written \emph{before} the dictionary. |
| 853 | +The~\pythonil{**}\pythonIdx{**!function parameter} is telling \python\ to unpack the dictionary this way. |
| 854 | +Second, default argument values still apply here. |
| 855 | +We did not specify a value for~\pythonil{mu}. |
| 856 | +This means that \pythonil{mu} will have value~\pythonil{0.0} in this function call. |
| 857 | + |
| 858 | +Maybe we do not want to pass in the arguments by parameter names but simply by position. |
| 859 | +Actually, we have always done it like this in the past. |
834 | 860 | Then, we can construct a sequence, e.g., a \pythonilIdx{list} or \pythonilIdx{tuple} with the parameter values. |
835 | 861 | Of course, \pythonilsIdx{list} and \pythonilsIdx{tuple} do not store key-value relationships, only values at positions. |
836 | 862 | We could create a tuple~\pythonil{args_tuple} with the value~\pythonil{(-2.0, 7.0, 3.0)}. |
837 | | -Then, invoking \pythonil{pdf(*args_tuple)} will basically fill in the three values in their into the parameters, i.e., will be equivalent to~\pythonil{pdf(-2.0, 7.0, 3.0)}. |
| 863 | + |
| 864 | +Then, we invoke \pythonil{pdf} like this:~\pythonil{pdf(*args_tuple)}. |
| 865 | +This will basically fill in the (three) values from the \pythonil{tuple} one by one into the parameter slots of the function. |
| 866 | +In other words, this is equivalent to writing~\pythonil{pdf(-2.0, 7.0, 3.0)}. |
| 867 | + |
838 | 868 | This time, only a single wildcard~\pythonil{*}\pythonIdx{*!function parameter}\pythonIdx{function!argument!*}\pythonIdx{function!parameter!*} is placed before~\pythonil{args_tuple}. |
839 | 869 | We can also pass the parameters in by \inQuotes{unpacking} a list. |
840 | 870 | In our example \cref{lst:functions:use_normal_pdf}, we create the list \pythonil{args_list = [2.0, 3.0]}. |
|
845 | 875 | What do we need default parameter values for? |
846 | 876 | Well, in some cases, you may want to enable a user to \inQuotes{customize} your functions. |
847 | 877 | A typical example is the \href{https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.plot.html}{\pythonilIdx{plot} method} of the \pythonilIdx{Axes} object provided by popular \matplotlib\ library. |
848 | | -You will normally provide a sequence of x-\ and y\nobreakdashes-coordinates to this function it will draw a line which goes through all the points specified this way. |
| 878 | +You will normally provide sequences of x-\ and y\nobreakdashes-coordinates to this function it will draw a line which goes through all the points specified this way. |
849 | 879 | However, you can also optionally specify a color for the line, markers to be painted at the points, line dash style, a label, colors and sizes for the markers, a z\nobreakdashes-order to be used if multiple lines are drawn, and so on, and so on. |
850 | 880 | The use of default arguments allows the function call to be relatively simple in most cases, while still allowing the user to do more complex formatting if need be. |
851 | 881 |
|
852 | 882 | From this example, we can also directly extrapolate a use case for building the arguments of a function in a dictionary. |
853 | 883 | Imagine that you write an own function that uses one of the plotting methods of \matplotlib. |
854 | 884 | Let's say that your function does a plot call where it provides ten parameter values. |
855 | 885 | However, you have one special case where you need to provide one more parameter, maybe a line dash style that you otherwise do not need to provide. |
856 | | -Then, you could have some \pythonil{if} in your code that branches to do the ten-parameter-call in one case and the eleven-parameter-call in the other. |
| 886 | +Then, you could have some \pythonil{if ... else} in your code that branches to do the ten-parameter-call in one case and the eleven-parameter-call in the other. |
857 | 887 | This means that a rather complex function call appears twice in a very similar manner. |
858 | | -If you instead construct the parameters in a dictionary and in the \pythonil{if} branch just add the eleventh parameter if need be, your code will become much simpler.% |
859 | | -% |
| 888 | +If you instead construct a dictionary with the ten parameters. |
| 889 | +In the \pythonil{if} branch just add the eleventh parameter if need be. |
| 890 | +We now only need a single function call using the double-wildcard method. |
| 891 | +The code will become much simpler. |
| 892 | +The difference between the two cases will also be much more obvious. |
| 893 | +We have reduced the potential for errors significantly. |
| 894 | + |
| 895 | +With the default parameter values and function calls via \pythonil{*} and \pythonil{**}, we learned two more aspects that make it easier for us to work with functions in \python. |
| 896 | +With the default values, we can now design more versatile function-based \pglspl{API} that allow the user of our function to provide values for some parameters, while leaving others at reasonable default settings. |
| 897 | +By constructing arguments in collections, we can create code that is easier to read when we deal with functions with many arguments that may be called slightly differently depending on the situation.% |
860 | 898 | \FloatBarrier% |
861 | 899 | \endhsection% |
862 | 900 | % |
|
0 commit comments