From 4ef55e4d52e06d071354bd7f6f6ba0a86243fc0e Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Sat, 10 Sep 2022 18:19:22 +0200 Subject: Add reveal.js/dist/theme/jupyter.css and reveal.js/python-oo.html --- reveal.js/python.html | 264 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 256 insertions(+), 8 deletions(-) (limited to 'reveal.js/python.html') 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 @@

Preliminary plan


@@ -231,6 +231,8 @@ # load, use and unload the virtual environment source my_virtual_env/bin/activate pip install sqlalchemy + # install package from a custom repository (https://artifactory.fifty.eu) + pip install --index-url=https://artifactory.fifty.eu/artifactory/api/pypi/pypi/simple/ odin-data-access deactivate # one can alternatively use the python "wrapper" of the virtual env @@ -344,22 +346,173 @@ def addl(a, b): """Function for adding two lists""" result = a + b - a += [5] + a += [5] # in this case equal to: a.append(5) return result a_param = [9] b_param = [-2] addl(a_param, b_param) # Out: [9, -2] # lists are mutable and a_param will be changed - print(a_param) # Out: [5] + print(a_param) # Out: [9, 5]
-

Functions

+

Functions (cont ...)


-

To be continued in the next episode....

+

Parameters and arguments

+
+            
+              def add(a, b):
+                  """Function for adding integers"""
+                  return a + b
+              my_result = add(2, 5)  # positional arguments (parameters)
+              my_result = add(b=5, a=2)  # keyword arguments (parameters)
+              my_tuple = (2, 5)
+              my_dict = {'b': 5, 'a': 2}
+              my_result = add(*my_tuple)  # unpacked and assigned to the positional arguments
+              my_result = add(**my_dict)  # unpacked and assigned to the kw. arguments
+
+              def add(a, b=5):
+                  """Function for adding integers"""
+                  return a + b
+              my_result = add(2)
+              # ... and the rest of the examples above will work
+
+              def add(a, *args, **kwargs):
+                  """Function for adding integers"""
+                  if args:
+                      b = args[0]
+                  elif 'b' in kwargs:
+                      b = kwargs['b']
+                  return a + b
+              my_result = add(2, 5, 9, 11)  # 5 assigned to args[0]
+              my_result = add(2, b=5, c=9, d=11)  # 5 assigned to kwargs['b']
+              my_result = add(2, b=5, 9, 11)  # SyntaxError: positional argument follows keyword argument
+            
+          
+
+ +
+

Functions (cont ...)

+
+

Docstrings annotations and other hints

+
+            
+              def decrypt(password: str, edata: str) -> str:
+                  """
+                  Decrypts `edata` using `password`.
+
+                  `edata` is in the following format:
+                  enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`
+
+                  :param password: The password to generate the key with
+                  :type password: str
+
+                  :param edata: The data to be decrypted
+                  :type edata: str
+
+                  :raises EtoolkitInstanceError: If the encryption format is unsupported
+
+                  :return: The output string / decrypted data
+                  :rtype: str
+                  """
+                  if not edata.startswith('enc-val$1$'):
+                      raise EtoolkitInstanceError('Unsupported encryption format')
+                  # some more code magic coming after....
+                  # ...
+                  # ..
+                  return decrypted_str
+            
+          
+
+ +
+

Functions (cont ...)

+
+

Using typing for more advanced / flexible hinting

+
+            
+              import typing
+
+              Basestring = typing.Union[str, bytes]
+
+              def decrypt(password: Basestring, edata: str) -> str:
+                  pass
+
+
+              from typing import Union
+
+              def decrypt(password: Union[str, bytes], edata: str) -> str:
+                  """Generic documentation. No need for pass"""
+
+              # Python >= 3.10 only
+              def decrypt(password: str | bytes, edata: str) -> str:
+                  """Generic documentation. No need for pass"""
+            
+          
+
+ +
+

Functions (cont ...)

+

Functions as parameters / arguments, lambdas and returning multiple values

+
+            
+              def fetch_the_first_letter(input_str: str) -> str:
+                  """Fetches the first letter of the string input_str or 'x'""""
+                  try:
+                      return input_str[0]
+                  except Exception:
+                      return 'x'
+              letter_list = list(map(fetch_the_first_letter, ['foo', 'bar', 'test']))  # Out: ['f', 'b', 't']
+            
+          
+

Small anonymous functions can be created with the lambda keyword

+
+            
+              letter_list = list(map(lambda x: x[0], ['foo', 'bar', 'test']))  # Out: ['f', 'b', 't']
+            
+          
+

Functions in Python are callable objects. Callable objects can be created by defining the __call__ method. More on that later in the course...

+

A function can return multiple values by implicitly returning a tuple:

+
+            
+              def square_cube(x):
+                  """returns x, x^2 and x^3"""
+                  return x, x**2, x**3
+              numbers = square_cube(5)  # Out: (5, 25, 125)
+              num, sqnum, cbnum = square_cube(5)  # unpacking the tuple
+            
+          
+
+ +
+

Functions (cont ...)

+
+

Enclosing and nested functions

+

Can be used as:

+ +
+            
+              def get_multiplier_of(base: int) -> str:
+                  """the function enclosing its nested functions"""
+    
+                  def multiplier_function(x):
+                      """a nested function"""
+                      return base * x
+
+                  return multiplier_function
+
+              times3 = get_multiplier_of(3)
+              times5 = get_multiplier_of(5)
+              print(times3(3))  # Out: 9
+              print(times5(3))  # Out: 15
+            
+          
@@ -399,7 +552,102 @@
-

Q & A

+

Decorators

+

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.

+
+            
+              def my_decorator(func):
+
+                  def decorated():
+                      print('Doing something before the decorated function')
+                      retval = func()
+                      print('Doing something after the decorated function')
+                      return retval
+                  return decorated
+
+              def my_function():
+                  print('Alice')
+
+              my_function = my_decorator(my_function)
+              my_function()
+            
+          
+

... may be dificult to read / understand, while:

+
+            
+              @my_decorator
+              def my_function():
+                  print('Alice')
+
+              my_function()
+            
+          
+

... may be easier

+
+ +
+

Decorators (cont ...)

+
+

A complete example

+
+            
+              import sys
+              from functools import wraps
+
+              def requires_access(access_secret: str):
+
+                  def api_access_decorator(f):
+
+                      @wraps(f)
+                      def decorated(*args, **kwargs):
+                          if 'secret' not in kwargs:
+                              sys.exit('No secret provided')
+                          if not kwargs['secret'] or kwargs['secret'] != access_secret:
+                              sys.exit("Secret doesn't match")
+                          # return f(args[0], **kwargs)
+                          return f(*args, **kwargs)
+
+                      return decorated
+
+                  return api_access_decorator
+
+
+              @requires_access(access_secret='b28cfeaa65b73cf')
+              def sensitive_function(data, **kwargs):
+                  """very sensitive function"""
+                  db.save(data)
+            
+          
+
+ +
+

String formatting

+
+

The old ways...

+
+            
+              f = 6.57865
+              i = 27
+              s = 'another string'
+
+
+              '%s - %d - %5.2f' % (s, i, f)  # Out: 'another string - 27 -  6.58'
+
+              # still used in:
+              logger.debug("%d - %s", event.id, message)
+
+
+              '{} - {} - {:5.2f}'.format(s, i, f)  # implicit
+              '{0} - {1} - {2:5.2f}'.format(s, i, f)  # explicit
+              '{my_str} - {i} - {fl:5.2f}'.format(my_str=s, fl=f, i=i)  # keyword
+              # Out: 'another string - 27 -  6.58'
+
+
+              # modern Python >= 3.6 f-strings
+              f'{s} - {i} - {f:5.2f}'  # Out: 'another string - 27 -  6.58'
+            
+          
+

See https://docs.python.org/3/library/string.html#formatspec for the complete format specification

-- cgit v1.3