Diferències
Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.
| Ambdós costats versió prèvia Revisió prèvia | |||
| info:cursos:pue:python-pcpp1:m1:2.3 [05/11/2023 12:32] – suprimit - edició externa (Unknown date) 127.0.0.1 | info:cursos:pue:python-pcpp1:m1:2.3 [05/11/2023 12:32] (actual) – ↷ Page moved from info:cursos:pue:python-pcpp1:2.3 to info:cursos:pue:python-pcpp1:m1:2.3 mate | ||
|---|---|---|---|
| Línia 1: | Línia 1: | ||
| + | = 2.3 Extended function argument syntax | ||
| + | When we talk about function arguments, we should recall the following facts: | ||
| + | * some functions can be invoked without arguments; | ||
| + | * functions may require a specific number of arguments with no exclusions; we have to pass a required number of arguments in an imposed order to follow function definition; | ||
| + | * functions might have already defined default values for some parameters, so we do not have to pass all arguments as missing arguments, complete with defaults; parameters with default values are presented as keyword parameters; | ||
| + | * we can pass arguments in any order if we are assigning keywords to all argument values, otherwise positional ones are the first ones on the arguments list. | ||
| + | |||
| + | Now let’s get familiar with the functions that can accept any arbitrary number of positional arguments and keyword arguments. | ||
| + | |||
| + | So far we've been using a few such functions, but we haven' | ||
| + | |||
| + | The most basic example is a **print()** function: | ||
| + | |||
| + | <code python> | ||
| + | print() | ||
| + | print(3) | ||
| + | print(1, 20, 10) | ||
| + | print(' | ||
| + | </ | ||
| + | |||
| + | All presented invocations of the **print()** function end correctly, and the argument values are passed to the standard output. | ||
| + | |||
| + | Another interesting example can be presented with a list function: | ||
| + | <code python> | ||
| + | a_list = list() | ||
| + | b_list = list(10, 20, 43, 54, 23, 23, 34, 23, 2) | ||
| + | |||
| + | print(a_list) | ||
| + | print(b_list) | ||
| + | </ | ||
| + | Similarly to the **print()** function, the **list()** function invocation accepts an arbitrary number of arguments. In fact, there are many, many other functions that accept arbitrary numbers of parameters, and you can spot them easily when reading third-party code. | ||
| + | |||
| + | How is it possible for Python functions to deal with a variable number of arguments? Can we prepare such functions ourselves? | ||
| + | |||
| + | The answer is yes, but it may look strange at first glance: //%%*%%args and %%**%%kwargs// | ||
| + | |||
| + | These two special identifiers (named *args and %%**%%kwargs) should be put as the last two parameters in a function definition. Their names could be changed because it is just a convention to name them ' | ||
| + | |||
| + | Those two special parameters are responsible for handling any number of additional arguments (placed next after the expected arguments) passed to a called function: | ||
| + | * ** *args** – refers to a tuple of all additional, not explicitly expected positional arguments, so arguments passed without keywords and passed next after the expected arguments. In other words, *args collects all unmatched positional arguments; | ||
| + | * **%%**%%kwargs** (keyword arguments) – refers to a dictionary of all unexpected arguments that were passed in the form of keyword=value pairs. Likewise, %%**%%kwargs collects all unmatched keyword arguments. | ||
| + | |||
| + | In Python, asterisks are used to denote that args and kwargs parameters are not ordinary parameters and should be unpacked, as they carry multiple items. | ||
| + | |||
| + | If you’ve ever programmed in the C or C++ languages, then you should remember that the asterisk character has another meaning (it denotes a pointer) which could be misleading for you. | ||
| + | |||
| + | Let's run the Python code presented in the right pane and study the output: | ||
| + | <code python> | ||
| + | def combiner(a, b, *args, **kwargs): | ||
| + | print(a, type(a)) | ||
| + | print(b, type(b)) | ||
| + | print(args, type(args)) | ||
| + | print(kwargs, | ||
| + | |||
| + | |||
| + | combiner(10, | ||
| + | |||
| + | </ | ||
| + | <code ; output> | ||
| + | 10 <class ' | ||
| + | 20 <class ' | ||
| + | (40, 60, 30) <class ' | ||
| + | {' | ||
| + | </ | ||
| + | |||
| + | As you can see, the function' | ||
| + | |||
| + | Now if you take a look at the built-in print() function definition, it becomes clear how this function can accept any number of arguments, and why there is an asterisk before one of the parameters: | ||
| + | <code python> | ||
| + | def print(self, *args, sep=' ', end=' | ||
| + | </ | ||
| + | | ||
| + | == Extended function argument syntax – forwarding arguments to other functions | ||
| + | When you want to forward arguments received by your very smart and universal function (defined with *args and %%**%%kwargs, | ||
| + | |||
| + | <code python> | ||
| + | def combiner(a, b, *args, **kwargs): | ||
| + | super_combiner(*args, | ||
| + | |||
| + | def super_combiner(*my_args, | ||
| + | print(' | ||
| + | print(' | ||
| + | |||
| + | combiner(10, | ||
| + | </ | ||
| + | <code ; output> | ||
| + | my_args: (40, 60, 30) | ||
| + | my_kwargs {' | ||
| + | </ | ||
| + | Pay attention how the **super_combiner()** function is called – it’s called with genuine arguments so those arguments can be handled in the same way as they are handled by the combiner function. | ||
| + | |||
| + | If we remove the asterisks from the function call, then both tuple and dictionary would be captured by **my_args**, | ||
| + | |||
| + | <code python> | ||
| + | def combiner(a, b, *args, **kwargs): | ||
| + | super_combiner(*args, | ||
| + | |||
| + | def super_combiner(*my_args, | ||
| + | print(' | ||
| + | print(' | ||
| + | |||
| + | combiner(10, | ||
| + | </ | ||
| + | <code ; output> | ||
| + | my_args: ((40, 60, 30), {' | ||
| + | my_kwargs {} | ||
| + | </ | ||
| + | <code python> | ||
| + | def combiner(a, b, *args, **kwargs): | ||
| + | print(a, type(a)) | ||
| + | print(b, type(b)) | ||
| + | print(args, type(args)) | ||
| + | print(kwargs, | ||
| + | |||
| + | |||
| + | combiner(10, | ||
| + | </ | ||
| + | <code ; output> | ||
| + | 10 <class ' | ||
| + | 20 <class ' | ||
| + | (40, 60, 30) <class ' | ||
| + | {' | ||
| + | </ | ||
| + | The last example in this section shows how to combine *args, a key word, and %%**%%kwargs in one definition: | ||
| + | <code python> | ||
| + | def combiner(a, b, *args, c=20, **kwargs): | ||
| + | super_combiner(c, | ||
| + | def super_combiner(my_c, | ||
| + | print(' | ||
| + | print(' | ||
| + | print(' | ||
| + | combiner(1, ' | ||
| + | </ | ||
| + | <code ; output> | ||
| + | my_args: (1, 1) | ||
| + | my_c: 2 | ||
| + | my_kwargs {' | ||
| + | </ | ||
| + | As you can see, Python offers complex parameter handling: | ||
| + | |||
| + | * positional arguments (a,b) are distinguished from all other positional arguments (args) | ||
| + | * the keyword ' | ||
| + | Now that we know the purpose of special parameters, we can move on to decorators that make intensive use of those parameters. | ||