diff options
Diffstat (limited to 'reveal.js/python.html')
| -rw-r--r-- | reveal.js/python.html | 264 |
1 files changed, 256 insertions, 8 deletions
diff --git a/reveal.js/python.html b/reveal.js/python.html index 5190dfb..d27cd49 100644 --- a/reveal.js/python.html +++ b/reveal.js/python.html | |||
| @@ -44,9 +44,9 @@ | |||
| 44 | <h2>Preliminary plan</h2> | 44 | <h2>Preliminary plan</h2> |
| 45 | </br> | 45 | </br> |
| 46 | <ul> | 46 | <ul> |
| 47 | <li><p>Basics: About the language, the Python eco-system, types, scopes, modules and functions</p></li> | 47 | <li><p><b>Basics: About the language, the Python eco-system, types, modules, functions, scopes, decorators, string formatting</b></p></li> |
| 48 | <li><p>Control flow: if / for / while / try, iterators, decorators, "tactical programming" tips</p></li> | 48 | <li><p>Object-oriented programming in Python: How Python "really works"</p></li> |
| 49 | <li><p>Object orientation: How Python "really works"</p></li> | 49 | <li><p>Control flow: if / for / while / try, iterators, "tactical programming" tips</p></li> |
| 50 | <li><p>A brief tour through Python's standard library</p></li> | 50 | <li><p>A brief tour through Python's standard library</p></li> |
| 51 | <li><p>Code design and best practices: How to design your code</p></li> | 51 | <li><p>Code design and best practices: How to design your code</p></li> |
| 52 | </ul> | 52 | </ul> |
| @@ -231,6 +231,8 @@ | |||
| 231 | # load, use and unload the virtual environment | 231 | # load, use and unload the virtual environment |
| 232 | source my_virtual_env/bin/activate | 232 | source my_virtual_env/bin/activate |
| 233 | pip install sqlalchemy | 233 | pip install sqlalchemy |
| 234 | # install package from a custom repository (https://artifactory.fifty.eu) | ||
| 235 | pip install --index-url=https://artifactory.fifty.eu/artifactory/api/pypi/pypi/simple/ odin-data-access | ||
| 234 | deactivate | 236 | deactivate |
| 235 | 237 | ||
| 236 | # one can alternatively use the python "wrapper" of the virtual env | 238 | # one can alternatively use the python "wrapper" of the virtual env |
| @@ -344,22 +346,173 @@ | |||
| 344 | def addl(a, b): | 346 | def addl(a, b): |
| 345 | """Function for adding two lists""" | 347 | """Function for adding two lists""" |
| 346 | result = a + b | 348 | result = a + b |
| 347 | a += [5] | 349 | a += [5] # in this case equal to: a.append(5) |
| 348 | return result | 350 | return result |
| 349 | 351 | ||
| 350 | a_param = [9] | 352 | a_param = [9] |
| 351 | b_param = [-2] | 353 | b_param = [-2] |
| 352 | addl(a_param, b_param) # Out: [9, -2] | 354 | addl(a_param, b_param) # Out: [9, -2] |
| 353 | # lists are mutable and a_param will be changed | 355 | # lists are mutable and a_param will be changed |
| 354 | print(a_param) # Out: [5] | 356 | print(a_param) # Out: [9, 5] |
| 355 | </code> | 357 | </code> |
| 356 | </pre> | 358 | </pre> |
| 357 | </section> | 359 | </section> |
| 358 | 360 | ||
| 359 | <section> | 361 | <section> |
| 360 | <h2>Functions</h2> | 362 | <h2>Functions (cont ...)</h2> |
| 363 | </br> | ||
| 364 | <h3>Parameters and arguments</h3> | ||
| 365 | <pre data-id="code-animation"> | ||
| 366 | <code class="python" data-trim type="text/template"> | ||
| 367 | def add(a, b): | ||
| 368 | """Function for adding integers""" | ||
| 369 | return a + b | ||
| 370 | my_result = add(2, 5) # positional arguments (parameters) | ||
| 371 | my_result = add(b=5, a=2) # keyword arguments (parameters) | ||
| 372 | my_tuple = (2, 5) | ||
| 373 | my_dict = {'b': 5, 'a': 2} | ||
| 374 | my_result = add(*my_tuple) # unpacked and assigned to the positional arguments | ||
| 375 | my_result = add(**my_dict) # unpacked and assigned to the kw. arguments | ||
| 376 | |||
| 377 | def add(a, b=5): | ||
| 378 | """Function for adding integers""" | ||
| 379 | return a + b | ||
| 380 | my_result = add(2) | ||
| 381 | # ... and the rest of the examples above will work | ||
| 382 | |||
| 383 | def add(a, *args, **kwargs): | ||
| 384 | """Function for adding integers""" | ||
| 385 | if args: | ||
| 386 | b = args[0] | ||
| 387 | elif 'b' in kwargs: | ||
| 388 | b = kwargs['b'] | ||
| 389 | return a + b | ||
| 390 | my_result = add(2, 5, 9, 11) # 5 assigned to args[0] | ||
| 391 | my_result = add(2, b=5, c=9, d=11) # 5 assigned to kwargs['b'] | ||
| 392 | my_result = add(2, b=5, 9, 11) # SyntaxError: positional argument follows keyword argument | ||
| 393 | </code> | ||
| 394 | </pre> | ||
| 395 | </section> | ||
| 396 | |||
| 397 | <section> | ||
| 398 | <h2>Functions (cont ...)</h2> | ||
| 399 | </br> | ||
| 400 | <h3>Docstrings annotations and other hints</h3> | ||
| 401 | <pre data-id="code-animation"> | ||
| 402 | <code class="python" data-trim type="text/template"> | ||
| 403 | def decrypt(password: str, edata: str) -> str: | ||
| 404 | """ | ||
| 405 | Decrypts `edata` using `password`. | ||
| 406 | |||
| 407 | `edata` is in the following format: | ||
| 408 | enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data` | ||
| 409 | |||
| 410 | :param password: The password to generate the key with | ||
| 411 | :type password: str | ||
| 412 | |||
| 413 | :param edata: The data to be decrypted | ||
| 414 | :type edata: str | ||
| 415 | |||
| 416 | :raises EtoolkitInstanceError: If the encryption format is unsupported | ||
| 417 | |||
| 418 | :return: The output string / decrypted data | ||
| 419 | :rtype: str | ||
| 420 | """ | ||
| 421 | if not edata.startswith('enc-val$1$'): | ||
| 422 | raise EtoolkitInstanceError('Unsupported encryption format') | ||
| 423 | # some more code magic coming after.... | ||
| 424 | # ... | ||
| 425 | # .. | ||
| 426 | return decrypted_str | ||
| 427 | </code> | ||
| 428 | </pre> | ||
| 429 | </section> | ||
| 430 | |||
| 431 | <section> | ||
| 432 | <h2>Functions (cont ...)</h2> | ||
| 433 | </br> | ||
| 434 | <h3>Using <em>typing</em> for more advanced / flexible hinting</h3> | ||
| 435 | <pre data-id="code-animation"> | ||
| 436 | <code class="python" data-trim type="text/template"> | ||
| 437 | import typing | ||
| 438 | |||
| 439 | Basestring = typing.Union[str, bytes] | ||
| 440 | |||
| 441 | def decrypt(password: Basestring, edata: str) -> str: | ||
| 442 | pass | ||
| 443 | |||
| 444 | |||
| 445 | from typing import Union | ||
| 446 | |||
| 447 | def decrypt(password: Union[str, bytes], edata: str) -> str: | ||
| 448 | """Generic documentation. No need for pass""" | ||
| 449 | |||
| 450 | # Python >= 3.10 only | ||
| 451 | def decrypt(password: str | bytes, edata: str) -> str: | ||
| 452 | """Generic documentation. No need for pass""" | ||
| 453 | </code> | ||
| 454 | </pre> | ||
| 455 | </section> | ||
| 456 | |||
| 457 | <section> | ||
| 458 | <h2>Functions (cont ...)</h2> | ||
| 459 | <h4>Functions as parameters / arguments, lambdas and returning multiple values</h4> | ||
| 460 | <pre data-id="code-animation"> | ||
| 461 | <code class="python" data-trim type="text/template"> | ||
| 462 | def fetch_the_first_letter(input_str: str) -> str: | ||
| 463 | """Fetches the first letter of the string input_str or 'x'"""" | ||
| 464 | try: | ||
| 465 | return input_str[0] | ||
| 466 | except Exception: | ||
| 467 | return 'x' | ||
| 468 | letter_list = list(map(fetch_the_first_letter, ['foo', 'bar', 'test'])) # Out: ['f', 'b', 't'] | ||
| 469 | </code> | ||
| 470 | </pre> | ||
| 471 | <p>Small anonymous functions can be created with the <em>lambda</em> keyword</p> | ||
| 472 | <pre data-id="code-animation"> | ||
| 473 | <code class="python" data-trim type="text/template"> | ||
| 474 | letter_list = list(map(lambda x: x[0], ['foo', 'bar', 'test'])) # Out: ['f', 'b', 't'] | ||
| 475 | </code> | ||
| 476 | </pre> | ||
| 477 | <p>Functions in Python are <em>callable objects</em>. Callable objects can be created by defining the <em>__call__</em> method. More on that later in the course...</p> | ||
| 478 | <p>A function can return multiple values by implicitly returning a tuple:</p> | ||
| 479 | <pre data-id="code-animation"> | ||
| 480 | <code class="python" data-trim type="text/template"> | ||
| 481 | def square_cube(x): | ||
| 482 | """returns x, x^2 and x^3""" | ||
| 483 | return x, x**2, x**3 | ||
| 484 | numbers = square_cube(5) # Out: (5, 25, 125) | ||
| 485 | num, sqnum, cbnum = square_cube(5) # unpacking the tuple | ||
| 486 | </code> | ||
| 487 | </pre> | ||
| 488 | </section> | ||
| 489 | |||
| 490 | <section> | ||
| 491 | <h2>Functions (cont ...)</h2> | ||
| 361 | </br> | 492 | </br> |
| 362 | <h3>To be continued in the next episode....</h3> | 493 | <h3>Enclosing and nested functions</h3> |
| 494 | <p>Can be used as:</p> | ||
| 495 | <ul> | ||
| 496 | <li>regular functions within functions</li> | ||
| 497 | <li>dynamic function factories</li> | ||
| 498 | </ul> | ||
| 499 | <pre data-id="code-animation"> | ||
| 500 | <code class="python" data-trim type="text/template"> | ||
| 501 | def get_multiplier_of(base: int) -> str: | ||
| 502 | """the function enclosing its nested functions""" | ||
| 503 | |||
| 504 | def multiplier_function(x): | ||
| 505 | """a nested function""" | ||
| 506 | return base * x | ||
| 507 | |||
| 508 | return multiplier_function | ||
| 509 | |||
| 510 | times3 = get_multiplier_of(3) | ||
| 511 | times5 = get_multiplier_of(5) | ||
| 512 | print(times3(3)) # Out: 9 | ||
| 513 | print(times5(3)) # Out: 15 | ||
| 514 | </code> | ||
| 515 | </pre> | ||
| 363 | </section> | 516 | </section> |
| 364 | 517 | ||
| 365 | <section> | 518 | <section> |
| @@ -399,7 +552,102 @@ | |||
| 399 | </section> | 552 | </section> |
| 400 | 553 | ||
| 401 | <section> | 554 | <section> |
| 402 | <h1>Q & A</h1> | 555 | <h2>Decorators</h2> |
| 556 | <p>Decorators can be used to modify the behavior of the objects they decorate. Decorators can be implemented either by using classes or by using nested functions.</p> | ||
| 557 | <pre data-id="code-animation"> | ||
| 558 | <code class="python" data-trim type="text/template"> | ||
| 559 | def my_decorator(func): | ||
| 560 | |||
| 561 | def decorated(): | ||
| 562 | print('Doing something before the decorated function') | ||
| 563 | retval = func() | ||
| 564 | print('Doing something after the decorated function') | ||
| 565 | return retval | ||
| 566 | return decorated | ||
| 567 | |||
| 568 | def my_function(): | ||
| 569 | print('Alice') | ||
| 570 | |||
| 571 | my_function = my_decorator(my_function) | ||
| 572 | my_function() | ||
| 573 | </code> | ||
| 574 | </pre> | ||
| 575 | <p>... may be dificult to read / understand, while:</p> | ||
| 576 | <pre data-id="code-animation"> | ||
| 577 | <code class="python" data-trim type="text/template"> | ||
| 578 | @my_decorator | ||
| 579 | def my_function(): | ||
| 580 | print('Alice') | ||
| 581 | |||
| 582 | my_function() | ||
| 583 | </code> | ||
| 584 | </pre> | ||
| 585 | <p>... may be easier</p> | ||
| 586 | </section> | ||
| 587 | |||
| 588 | <section> | ||
| 589 | <h2>Decorators (cont ...)</h2> | ||
| 590 | </br> | ||
| 591 | <h3>A complete example</h3> | ||
| 592 | <pre data-id="code-animation"> | ||
| 593 | <code class="python" data-trim type="text/template"> | ||
| 594 | import sys | ||
| 595 | from functools import wraps | ||
| 596 | |||
| 597 | def requires_access(access_secret: str): | ||
| 598 | |||
| 599 | def api_access_decorator(f): | ||
| 600 | |||
| 601 | @wraps(f) | ||
| 602 | def decorated(*args, **kwargs): | ||
| 603 | if 'secret' not in kwargs: | ||
| 604 | sys.exit('No secret provided') | ||
| 605 | if not kwargs['secret'] or kwargs['secret'] != access_secret: | ||
| 606 | sys.exit("Secret doesn't match") | ||
| 607 | # return f(args[0], **kwargs) | ||
| 608 | return f(*args, **kwargs) | ||
| 609 | |||
| 610 | return decorated | ||
| 611 | |||
| 612 | return api_access_decorator | ||
| 613 | |||
| 614 | |||
| 615 | @requires_access(access_secret='b28cfeaa65b73cf') | ||
| 616 | def sensitive_function(data, **kwargs): | ||
| 617 | """very sensitive function""" | ||
| 618 | db.save(data) | ||
| 619 | </code> | ||
| 620 | </pre> | ||
| 621 | </section> | ||
| 622 | |||
| 623 | <section> | ||
| 624 | <h2>String formatting</h2> | ||
| 625 | </br> | ||
| 626 | <h3>The old ways...</h3> | ||
| 627 | <pre data-id="code-animation"> | ||
| 628 | <code class="python" data-trim type="text/template"> | ||
| 629 | f = 6.57865 | ||
| 630 | i = 27 | ||
| 631 | s = 'another string' | ||
| 632 | |||
| 633 | |||
| 634 | '%s - %d - %5.2f' % (s, i, f) # Out: 'another string - 27 - 6.58' | ||
| 635 | |||
| 636 | # still used in: | ||
| 637 | logger.debug("%d - %s", event.id, message) | ||
| 638 | |||
| 639 | |||
| 640 | '{} - {} - {:5.2f}'.format(s, i, f) # implicit | ||
| 641 | '{0} - {1} - {2:5.2f}'.format(s, i, f) # explicit | ||
| 642 | '{my_str} - {i} - {fl:5.2f}'.format(my_str=s, fl=f, i=i) # keyword | ||
| 643 | # Out: 'another string - 27 - 6.58' | ||
| 644 | |||
| 645 | |||
| 646 | # modern Python >= 3.6 f-strings | ||
| 647 | f'{s} - {i} - {f:5.2f}' # Out: 'another string - 27 - 6.58' | ||
| 648 | </code> | ||
| 649 | </pre> | ||
| 650 | <p>See <a href="https://docs.python.org/3/library/string.html#formatspec">https://docs.python.org/3/library/string.html#formatspec</a> for the complete format specification</p> | ||
| 403 | </section> | 651 | </section> |
| 404 | 652 | ||
| 405 | </div> | 653 | </div> |
