summaryrefslogtreecommitdiff
path: root/reveal.js/python.html
diff options
context:
space:
mode:
Diffstat (limited to 'reveal.js/python.html')
-rw-r--r--reveal.js/python.html680
1 files changed, 0 insertions, 680 deletions
diff --git a/reveal.js/python.html b/reveal.js/python.html
deleted file mode 100644
index d27cd49..0000000
--- a/reveal.js/python.html
+++ /dev/null
@@ -1,680 +0,0 @@
1<!doctype html>
2<html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <title>Introduction to Python</title>
6 <meta name="author" content="Simeon Simeonov">
7 <meta name="apple-mobile-web-app-capable" content="yes">
8 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9 <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
11 <link rel="stylesheet" href="dist/reset.css">
12 <link rel="stylesheet" href="dist/reveal.css">
13
14 <link rel="stylesheet" href="dist/theme/statnett.css" id="theme">
15
16 <!-- Theme used for syntax highlighting of code -->
17 <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme">
18 <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> -->
19 </head>
20 <body>
21 <div class="reveal">
22
23 <!-- Any section element inside of this container is displayed as a slide -->
24 <div class="slides">
25
26 <section>
27 <h2>Introduction to Python</h2>
28 <h4>Data Engineering @ Statnett</h4>
29 </br>
30 <p><small>Simeon Simeonov</small></p>
31 </section>
32
33 <section>
34 <h2>Goals</h2>
35 </br>
36 <ul>
37 <li><p>Present the Python programming language in a different way than <a href="https://docs.python.org">https://docs.python.org</a></p></li>
38 <li><p>Avoid information overload</p></li>
39 <li><p>Use examples and interaction rather than documents and slides</p></li>
40 </ul>
41 </section>
42
43 <section>
44 <h2>Preliminary plan</h2>
45 </br>
46 <ul>
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>Object-oriented programming in Python: 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>
51 <li><p>Code design and best practices: How to design your code</p></li>
52 </ul>
53 </section>
54
55 <section>
56 <h2>What is Python?</h2>
57 </br>
58 <ul>
59 <li><p>Python is an interpreted high-level general-purpose programming language - advanced through the Python Enhancement Proposal (PEP) process</p></li>
60 <li><p>CPython is the reference implementation of Python, written in C (alternatives: pypy, jython)</p></li>
61 <li><p>python - interpreter and interpreter shell (alternatives: ipython, bpython)</p></li>
62 <li><p>libpython</p></li>
63 <li><p>Calling C from Python: Cython, CFFI, ctypes</p></li>
64 </ul>
65 </section>
66
67 <section>
68 <h2>Philosophy</h2>
69 </br>
70 <pre data-id="code-animation">
71 <code class="bash" data-trim type="text/template">
72 $ python
73 </code>
74 </pre>
75 <pre data-id="code-animation">
76 <code class="python" data-trim type="text/template">
77 import this
78
79 # The Zen of Python, by Tim Peters
80
81 # Beautiful is better than ugly.
82 # Explicit is better than implicit.
83 # Simple is better than complex.
84 # Complex is better than complicated.
85 # Flat is better than nested.
86 # Sparse is better than dense.
87 # Readability counts.
88 # Special cases aren't special enough to break the rules.
89 # Although practicality beats purity.
90 # Errors should never pass silently.
91 # Unless explicitly silenced.
92 # In the face of ambiguity, refuse the temptation to guess.
93 # There should be one-- and preferably only one --obvious way to do it.
94 # Although that way may not be obvious at first unless you're Dutch.
95 # Now is better than never.
96 # Although never is often better than *right* now.
97 # If the implementation is hard to explain, it's a bad idea.
98 # If the implementation is easy to explain, it may be a good idea.
99 # Namespaces are one honking great idea -- let's do more of those!
100 </code>
101 </pre>
102 </section>
103
104 <section>
105 <h2>Built-in functions</h2>
106 </br>
107 <p>Few built-in functions.</p>
108 <p><a href="https://docs.python.org/3/library/functions.html">https://docs.python.org/3/library/functions.html</a></p>
109 <ul>
110 <li><em>dir([obj])</em> - returns a list of valid attributes for that object</li>
111 <li><em>id(obj)</em> - returns the "identity" of an object - an integer which is guaranteed to be unique</li>
112 <li><em>print(...)</em> - prints objects to a text stream</li>
113 <li><em>str(...)</em> - returns a string version of object</li>
114 <li><em>type(obj)</em> - returns the type of an object</li>
115 </ul>
116 </section>
117
118 <section>
119 <h2>Common built-in types</h2>
120 </br>
121 <p>Python uses <a href="https://en.wikipedia.org/wiki/Duck_typing"><em>duck typing</em></a> and has typed objects but untyped variable names.</p>
122 <p>Type constraints are not checked at compile time; rather, operations on an object may fail, signifying that the given object is not of a suitable type. Despite being dynamically-typed, Python is strongly-typed, forbidding operations that are not well-defined (for example, adding a number to a string) rather than silently attempting to make sense of them.</p>
123 <pre data-id="code-animation">
124 <code class="python" data-trim type="text/template">
125 s = 'foo' # this is a string / str, same as str('foo'), may be encoded, immutable
126 b = b'foo' # bytes, same as bytes('foo', 'utf-8'), may be decoded, immutable
127 i = 6 # int, same as int('6'), immutable
128 f = 0.1 # float, same as float('0.1'), immutable
129 b = False # bool, same as bool(0), bool(''), bool(None)... immutable / constant
130 n = None # NoneType, similar to 'null' in other languages, immutable / constant
131 l = [1, False, 'foo'] # list, same as list((1, False, 'foo'))
132 t = (1, False, 'foo') # tuple, same as tuple([1, False, 'foo']), immutable
133 d = {'foo': 1, 'bar': 8} # dict, same as dict(foo=1, bar=8), similar to hash in other languages
134 s = {'foo', 'bar', 1, 1, 4} # set, same as set(['foo', 'bar', 1, 1, 4]), removes duplicates
135 </code>
136 </pre>
137 <p>Classes, functions, modules, .... and even types are simply other types :)</p>
138 </section>
139
140 <section>
141 <h2>Modules</h2>
142 </br>
143 <p>A module is a file containing Python definitions and statements. The file name is the module name with the suffix <em>.py</em> appended. Within a module, the module's name (as a string) is available as the value of the global variable <em>__name__</em></p>
144 <p>When a module named <em>foo</em> is imported, the interpreter first searches for a built-in module with that name (<em>sys.builtin_module_names</em>). If not found, it then searches for a file named <em>foo.py</em> in a list of directories given by the variable <em>sys.path</em>. <em>sys.path</em> is initialized from these locations:</p>
145 <ul>
146 <li>the directory containing the input script (or the current directory when no file is specified)</li>
147 <li><em>PYTHONPATH</em> - env. variable - a list of directory names</li>
148 <li>the installation-dependent default locations</li>
149 </ul>
150
151 <p>The module is then imported only once and "cached" in <em>sys.modules</em></p>
152 </section>
153
154 <section>
155 <h2>Packages</h2>
156 </br>
157 <p><a href="https://docs.python.org/3/tutorial/modules.html#packages">Packages</a> are a way of structuring Python's module namespace by using "dotted module names"</p>
158 <p>The import statement combines two operations:</p>
159 <ul>
160 <li>it searches for the named module</li>
161 <li>it binds the results of that search to a name in the <em>local scope</em></li>
162 </ul>
163 <pre data-id="code-animation">
164 <code class="python" data-trim type="text/template">
165 # bar.py then bar/__init__.py will be considered, the first match executed and bound to 'bar'
166 import bar
167
168 import mymodule.foo # implicitly executes mymodule.py, mymodule/__init__.py and mymodule/foo/__init__.py
169
170 import numpy as np # will be bound as 'np' instead of 'numpy'. N.B. __name__ is still 'numpy'
171
172 import some.extremely.deep.path.Animal as Animal # "sacrifice" the namespace in the name of convinience
173
174 from sys import path # execute sys and only import the 'path' attribute into local scope as 'path'
175
176 # relative imports must be explicit in Python 3
177 from .othermodule import something # expects that current module and 'othermodule' are in the same
178 # package (containing __init__.py)
179
180 from sys import * # NO! Bad programming practice since 1879
181 </code>
182 </pre>
183 </section>
184
185 <section>
186 <h2>Creating and maintaining a Python environment</h2>
187 </br>
188 <p>Python's official package repository is <a href="https://pypi.org"><em>PyPi (https://pypi.org)</em></a>, while Python's official package installer is <a href="https://pypi.org/project/pip/"><em>pip (https://pypi.org/project/pip/)</em></a></p>
189 <p>A Python environment is the physical and logical arrangement of Python modules and packages. Several options exist:</p>
190 <ul>
191 <li>using a proper operating system :) (symlinks, real commercial support etc.)</li>
192 <li>using <em>venv</em></li>
193 <li>using higher level tools like <em>poetry</em></li>
194 <li>using a mixture / cocktail of all of the above :)</li>
195 </ul>
196 </section>
197
198 <section>
199 <h2>Creating and maintaining a Python environment (cont...)</h2>
200 </br>
201 <p>Desired qualities for a flexible Python environment:</p>
202 <ul>
203 <li>easy to create and (un)load</li>
204 <li>do not require extra privileges</li>
205 <li>don't repeat yourself (DRY)</li>
206 <li>easy to update without breaking the API</li>
207 <li>easy to debug</li>
208 <li>play nicely with the VCS (git)</li>
209 </ul>
210 </section>
211
212 <section>
213 <h3>Creating and maintaining a Python environment (cont...)</h3>
214 </br>
215 <p>Exploting the operating system can be done by:</p>
216 <ul>
217 <li>(re)defining <em>PYTHONPATH</em></li>
218 <li>using symlinks to point at packages placed at different locations</li>
219 </section>
220
221 <section>
222 <h3>Creating and maintaining a Python environment (cont...)</h3>
223 </br>
224 <p>Using <em>venv</em> can be done by directly invoking <em>python</em>:</p>
225 <pre data-id="code-animation">
226 <code class="bash" data-trim type="text/template">
227 # create a virtual environment
228 python -m venv my_virtual_env
229 python -m venv --system-site-packages my_virtual_env
230
231 # load, use and unload the virtual environment
232 source my_virtual_env/bin/activate
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
236 deactivate
237
238 # one can alternatively use the python "wrapper" of the virtual env
239 my_virtual_env/bin/python -m pip install sqlalchemy
240 </code>
241 </pre>
242 <p><em>--system-site-packages</em> will keep the original <em>site-packages</em> folders at the end of <em>sys.path</em></p>
243 </section>
244
245 <section>
246 <h3>Creating and maintaining a Python environment (cont...)</h3>
247 </br>
248 <p><em>Poetry</em> (<a href="https://python-poetry.org"><em>https://python-poetry.org</em></a>) is the prefered environment and dependency management tool at Statnett.</p>
249 <pre data-id="code-animation">
250 <code class="bash" data-trim type="text/template">
251 # create project and a virtual environment from scratch
252 poetry new my-project
253
254 # ... or use Poetry with an existing one
255 cd my-project
256 poetry init
257
258 # edit pyproject.toml for your needs (f.i. add dependencies, metadata ... etc),
259 # create virtual environment and install dependencies
260 poetry install
261 # finally commit your poetry.lock file to version control
262
263 # update all dependencies
264 poetry update
265 </code>
266 </pre>
267 <p>For more info: <a href="https://python-poetry.org/docs/basic-usage/"><em>https://python-poetry.org/docs/basic-usage/</em></a></p>
268 </section>
269
270 <section>
271 <h2>Mutables vs. immutables</h2>
272 <p>Immutable object is an object with a fixed value. Immutable objects include <em>bool, int, float, str, bytes</em> and <em>tuples</em>. Such an object cannot be altered. A new object has to be created if a different value has to be stored. They play an important role in places where a constant hash value is needed, for example as a key in a dictionary.</p>
273 <p>All objects that are not immutable are... mutable. All <em>hashable objects</em> <b>should</b> be immutable or use <em>id()</em>.</p>
274 <pre data-id="code-animation">
275 <code class="python" data-trim type="text/template">
276 i = 1
277 id(i) # returns f.i. 9788992
278 i += 1 # same as i = i + 1
279 id(i) # returns a different value, hence - a brand new object
280
281 s = 'Hello'
282 s += ' World' # s is now a different object
283 s[0] # 'H'
284 s[0] = 'h' # TypeError: 'str' object does not support item assignment
285
286 t = (1, 4) # tuple
287 l = [1, 4] # list
288 hash(t) # returns f.i. -6333845781340707986
289 hash(l) # TypeError: unhashable type: 'list'
290
291 s = 'the long and winding road'
292 s2 = 'the long and winding road'
293
294 # check if s and s2 are the same object:
295 id(s) # Out: 139858905258704
296 id(s2) # Out: 139858926037472
297
298 # the hash should be the same
299 hash(s) # Out: 7030216208569256362
300 hash(s2) # Out: 7030216208569256362
301 </code>
302 </pre>
303 </section>
304
305 <section>
306 <h2>Functions</h2>
307 </br>
308 <p>A function is a sequence of program instructions that performs a specific task, packaged as a unit.</p>
309 <p>Functions let you:</p>
310 <ul>
311 <li>reuse code across several programs / projects</li>
312 <li>minimize code duplication</li>
313 <li>devide larger programming tasks</li>
314 <li>hide implementation details</li>
315 <li>improve readability</li>
316 <li>improve traceability</li>
317 </ul>
318 <p>Function calls bring some overhead pushing / popping function-data into / from stack.</p>
319 <p>Important definitions (may have different meanings in different programming languages):</p>
320 <ul>
321 <li><em>parameter / formal parameter</em> - variable / data provided as input to the function</li>
322 <li><em>argument / actual parameter</em> - local variable / data to the given function</li>
323 </ul>
324 <p>The keyword <em>def</em> introduces a function definition.</p>
325 </section>
326
327 <section>
328 <h2>Functions (cont...)</h2>
329 </br>
330 <pre data-id="code-animation">
331 <code class="python" data-trim type="text/template">
332 def add(a, b): # - function definition / header
333 """Function for adding integers""" # - docstring
334 result = a + b
335 a = 5
336 return result # - function that does not contain return, implicitly returns None
337
338 a_param = 9
339 b_param = -2
340 add(a_param, b_param) # Out: 7
341
342 # integers are immutable and a_param will remain unchanged
343 print(a_param) # Out: 9
344
345
346 def addl(a, b):
347 """Function for adding two lists"""
348 result = a + b
349 a += [5] # in this case equal to: a.append(5)
350 return result
351
352 a_param = [9]
353 b_param = [-2]
354 addl(a_param, b_param) # Out: [9, -2]
355 # lists are mutable and a_param will be changed
356 print(a_param) # Out: [9, 5]
357 </code>
358 </pre>
359 </section>
360
361 <section>
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>
492 </br>
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>
516 </section>
517
518 <section>
519 <h2>Scopes in Python</h2>
520 </br>
521 <ul>
522 <li><em>local</em> - assigned names are local unless declared <em>global</em></li>
523 <li><em>enclosed</em> - the scope of the variable inside a function with a nested function</li>
524 <li><em>global - global for the current module</em></li>
525 <li><em>built-in</em></li>
526 </ul>
527 <p><em>locals()</em> and <em>globals()</em> return dicts of symbols for their respective scopes</p>
528 <pre data-id="code-animation">
529 <code class="python" data-trim type="text/template">
530 num1, num2 = 7, 8 # module globals
531
532 def print_numbers():
533 print(num1, num2) # OK, these are module globals
534 num3 = 100
535 print(num3) # prints 100, num3 is in the function (local) scope
536 global num4 # assignes / references num4 to / in the global scope
537 num4 = 99
538 id = 200 # new symbol in local scope
539 # id(num4) # will not yield the expected result (raises TypeError)
540
541 def print_numbers2():
542 print(num3) # OK, enclosed scope
543
544 print_numbers2() # prints 100
545
546 print_numbers()
547 # print(num3) # Raises NameError - why?
548 print(num4) # Prints 99 - why?
549 # print_numbers2() # Raises NameError
550 </code>
551 </pre>
552 </section>
553
554 <section>
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>
651 </section>
652
653 </div>
654 </div>
655
656 <script src="dist/reveal.js"></script>
657 <script src="plugin/zoom/zoom.js"></script>
658 <script src="plugin/notes/notes.js"></script>
659 <script src="plugin/search/search.js"></script>
660 <script src="plugin/markdown/markdown.js"></script>
661 <script src="plugin/highlight/highlight.js"></script>
662 <script>
663
664 // Also available as an ES module, see:
665 // https://revealjs.netlify.app/initialization/
666 Reveal.initialize({
667 controls: true,
668 progress: true,
669 center: true,
670 hash: true,
671
672 // Learn about plugins: https://revealjs.netlify.app/plugins/
673 plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ]
674 });
675 Reveal.configure({ pdfSeparateFragments: false });
676
677 </script>
678
679 </body>
680</html>