You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: lectures/python_advanced_features.md
+173Lines changed: 173 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -290,6 +290,179 @@ tags: [raises-exception]
290
290
max(y)
291
291
```
292
292
293
+
## `*` and `**` Operators
294
+
295
+
`*` and `**` are convenient and widely used tools to unpack lists and tuples and to allow users to define functions that take arbitrarily many arguments as input.
296
+
297
+
In this section, we will explore how to use them and distinguish their use cases.
298
+
299
+
300
+
### Unpacking Arguments
301
+
302
+
When we operate on a list of parameters, we often need to extract the content of the list as individual arguments instead of a collection when passing them into functions.
303
+
304
+
Luckily, the `*` operator can help us to unpack lists and tuples into [*positional arguments*](https://63a3119f7a9a1a12f59e7803--epic-agnesi-957267.netlify.app/functions.html#keyword-arguments) in function calls.
305
+
306
+
To make things concrete, consider the following examples:
307
+
308
+
Without `*`, the `print` function prints a list
309
+
310
+
```{code-cell} python3
311
+
l1 = ['a', 'b', 'c']
312
+
313
+
print(l1)
314
+
```
315
+
316
+
While the `print` function prints individual elements since `*` unpacks the list into individual arguments
317
+
318
+
```{code-cell} python3
319
+
print(*l1)
320
+
```
321
+
322
+
Unpacking the list using `*` into positional arguments is equivalent to defining them individually when calling the function
323
+
324
+
```{code-cell} python3
325
+
print('a', 'b', 'c')
326
+
```
327
+
328
+
However, `*` operator is more convenient if we want to reuse them again
329
+
330
+
```{code-cell} python3
331
+
l1.append('d')
332
+
333
+
print(*l1)
334
+
```
335
+
336
+
Similarly, `**` is used to unpack arguments.
337
+
338
+
The difference is that `**` unpacks *dictionaries* into *keyword arguments*.
339
+
340
+
`**` is often used when there are many keyword arguments we want to reuse.
341
+
342
+
For example, assuming we want to draw multiple graphs using the same graphical settings,
343
+
it may involve repetitively setting many graphical parameters, usually defined using keyword arguments.
344
+
345
+
In this case, we can use a dictionary to store these parameters and use `**` to unpack dictionaries into keyword arguments when they are needed.
346
+
347
+
Let's walk through a simple example together and distinguish the use of `*` and `**`
In this example, `*` unpacked the zipped parameters `βs` and the output of `generate_data` function stored in tuples,
401
+
while `**` unpacked graphical parameters stored in `legend_kargs` and `line_kargs`.
402
+
403
+
To summarize, when `*list`/`*tuple` and `**dictionary` are passed into *function calls*, they are unpacked into individual arguments instead of a collection.
404
+
405
+
The difference is that `*` will unpack lists and tuples into *positional arguments*, while `**` will unpack dictionaries into *keyword arguments*.
406
+
407
+
### Arbitrary Arguments
408
+
409
+
When we *define* functions, it is sometimes desirable to allow users to put as many arguments as they want into a function.
410
+
411
+
You might have noticed that the `ax.plot()` function could handle arbitrarily many arguments.
412
+
413
+
If we look at the [documentation](https://github.com/matplotlib/matplotlib/blob/v3.6.2/lib/matplotlib/axes/_axes.py#L1417-L1669) of the function, we can see the function is defined as
We found `*` and `**` operators again in the context of the *function definition*.
420
+
421
+
In fact, `*args` and `**kargs` are ubiquitous in the scientific libraries in Python to reduce redundancy and allow flexible inputs.
422
+
423
+
`*args` enables the function to handle *positional arguments* with a variable size
424
+
425
+
```{code-cell} python3
426
+
l1 = ['a', 'b', 'c']
427
+
l2 = ['b', 'c', 'd']
428
+
429
+
def arb(*ls):
430
+
print(ls)
431
+
432
+
arb(l1, l2)
433
+
```
434
+
435
+
The inputs are passed into the function and stored in a tuple.
436
+
437
+
Let's try more inputs
438
+
439
+
```{code-cell} python3
440
+
l3 = ['z', 'x', 'b']
441
+
arb(l1, l2, l3)
442
+
```
443
+
444
+
Similarly, Python allows us to use `**kargs` to pass arbitrarily many *keyword arguments* into functions
445
+
446
+
```{code-cell} python3
447
+
def arb(**ls):
448
+
print(ls)
449
+
450
+
# Note that these are keyword arguments
451
+
arb(l1=l1, l2=l2)
452
+
```
453
+
454
+
We can see Python uses a dictionary to store these keyword arguments.
455
+
456
+
Let's try more inputs
457
+
458
+
```{code-cell} python3
459
+
arb(l1=l1, l2=l2, l3=l3)
460
+
```
461
+
462
+
Overall, `*args` and `**kargs` are used when *defining a function*; they enable the function to take input with an arbitrary size.
463
+
464
+
The difference is that functions with `*args` will be able to take *positional arguments* with an arbitrary size, while `**kargs` will allow functions to take arbitrarily many *keyword arguments*.
0 commit comments