Skip to content

Commit

Permalink
🎨 Rearrange control flow section
Browse files Browse the repository at this point in the history
* Update boolean operators
* Rename if-elif-else
* Update exceptions
  • Loading branch information
veit committed Dec 28, 2024
1 parent 67a7a88 commit 3a95a8d
Show file tree
Hide file tree
Showing 26 changed files with 301 additions and 241 deletions.
8 changes: 4 additions & 4 deletions docs/appendix/checks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,8 @@ Checks
>>> print(personal_data[who])
60
:doc:`/control-flows/loops`
---------------------------
:doc:`/control-flow/loops`
--------------------------

* Removes all negative numbers from the list ``x = [ -2, -1, 0, 1, 2, 3]``.

Expand Down Expand Up @@ -532,8 +532,8 @@ Checks
>>> {x: x**3 for x in range(1, 5)}
{1: 1, 2: 8, 3: 27, 4: 64}
:doc:`/control-flows/exceptions`
--------------------------------
:doc:`/control-flow/exceptions`
-------------------------------

* Write code that receives two numbers and divides the first number by the
second. Check if the :class:`python3:ZeroDivisionError` occurs when the second
Expand Down
82 changes: 82 additions & 0 deletions docs/control-flow/boolean.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Boolean values and expressions
==============================

In Python, there are several ways to express Boolean values; the Boolean
constant ``False``, ``0``, the Python type :doc:`../types/none` and empty
values (for example the empty list ``[]`` or the empty string ``""``) are all
considered ``False``. The Boolean constant ``True`` and everything else is
considered ``True``.

``<``, ``<=``, ``==``, ``>``, ``>=``
compares values:

.. code-block:: pycon
>>> x = 3
>>> y = 3.0
>>> z = [3, 4, 5]
>>> x == y
True
However, you should never compare calculated floating point numbers with
each other:

.. code-block:: pycon
>>> u = 0.6 * 7
>>> v = 0.7 * 6
>>> u == v
False
>>> u
4.2
>>> v
4.199999999999999
``is``, ``is not``, ``in``, ``not in``
checks the identity:

.. code-block:: pycon
>>> x is y
False
>>> x is not y
True
>>> x in z
True
>>> id(x)
4375911432
>>> id(y)
4367574480
>>> id(z[0])
4375911432
If ``x`` and ``z[0]`` have the same ID in memory, this means that we are
referring to the same object in two places.

Most frequently, ``is`` and ``is not`` are used in conjunction with
:doc:`../types/none`:

.. code-block:: pycon
>>> x is None
False
>>> x is not None
True
The Python style guide in :pep:`8` says that you should use identity to
compare with :doc:`../types/none`. So you should never use ``x == None``,
but ``x is None`` instead.

``and``, ``not``, ``or``
are logical operators that we can use to link the above checks:

.. code-block:: pycon
>>> x is y and x is z[0]
False
>>> x is y or x is z[0]
True
>>> x is y and not x is z[0]
False
>>> x is z[0] and not x is y
True
40 changes: 40 additions & 0 deletions docs/control-flow/conditional.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Conditional statements
======================

The code block after the first true condition of an ``if`` or ``elif`` statement
is executed. If none of the conditions are true, the code block after the
``else`` is executed:

.. code-block:: pycon
:linenos:
>>> x = 1
>>> if x < 1:
... x = 2
... y = 3
... elif x > 1:
... x = 4
... y = 5
... else:
... x = 6
... y = 7
...
>>> x, y
(6, 7)
Python uses indentations to delimit blocks. No explicit delimiters such as
brackets or curly braces are required. Each block consists of one or more
statements separated by line breaks. All these statements must be at the same
indentation level.

Line 5
The ``elif`` statement looks like the ``if`` statement and works in the same
way, but with two important differences:

* ``elif`` is only allowed after an ``if`` statement or another ``elif``
statement
* you can use as many ``elif`` statements as you need

Line 8
The optional ``else`` clause denotes a code block that is only executed if
the other conditional blocks, ``if`` and ``elif``, are all false.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ class EmptyFileError(Exception):


filenames = ["myFile1.py", "nonExistent.py", "emptyFile.py", "myFile2.py"]

for file in filenames:
try:
f = open(file, "r")
line = f.readline()
if line == "":
f.close()
raise EmptyFileError(f"{file} is empty")
except IOError as error:
except OSError as error:
print(f"Cannot open file {file}: {error.strerror}")
except EmptyFileError as error:
print(error)
else:
print(f"{file}: {f.readline()}")
finally:
print("File", file, "processed")
f.close()
121 changes: 121 additions & 0 deletions docs/control-flow/exceptions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
Exceptions
==========

This section deals with exceptions, which are language functions that
specifically handle unusual circumstances during the execution of a programme.
The most common exception is error handling, but they can also be used
effectively for many other purposes. Python provides a comprehensive set of
exceptions, and you can define new exceptions for your own purposes.

An exception is an object that is automatically created by Python functions with
a :ref:`raise <python3:raise>` statement, for example with:

.. literalinclude:: exceptions.py
:language: python
:linenos:
:lines: 11-12
:lineno-start: 11

The :ref:`raise <python3:raise>` statement causes the Python programme to be
executed in a different way than is usually intended: The current call chain is
searched for a handler that can handle the generated exception. If such a
handler is found, it is called and can access the exception object to obtain
further information, as in our :class:`EmptyFileError` example:

.. literalinclude:: exceptions.py
:language: python
:linenos:
:lines: 1-2

This defines your own exception type, which inherits from the ``Exception`` base
type.

You can find an overview of the class hierarchy of built-in exceptions at
`Exception hierarchy
<https://docs.python.org/3/library/exceptions.html#exception-hierarchy>`_ in the
Python documentation. Each exception type is a Python class that inherits from
its parent exception type. For example, a ``ZeroDivisionError`` is also an
``ArithmeticError``, an ``Exception`` and also a ``BaseException`` by
inheritance. This hierarchy is intentional: most exceptions inherit from
``Exception``, and it is strongly recommended that all user-defined exceptions
also subclass ``Exception`` and not ``BaseException``:

It is possible to create different types of exceptions to reflect the actual
cause of the reported error or exceptional circumstance.

.. literalinclude:: exceptions.py
:language: python
:linenos:
:lines: 8-16
:lineno-start: 8

If an ``OSError`` or an ``EmptyFileError`` occurs in the ``try`` block during
the execution of :func:`open`, the corresponding ``except`` block is executed.

If no suitable exception handler is found, the programme terminates with an
error message. We therefore add ``else`` and ``finally`` to our
``try``-``except`` statements:

.. literalinclude:: exceptions.py
:language: python
:linenos:
:lines: 17-21
:lineno-start: 17

Now we can define a list of different file types so that our complete code looks
like this:

.. literalinclude:: exceptions.py
:language: python
:linenos:
:lines: 1-
:lineno-start: 1

Line 7
If an ``OSError`` or ``EmptyFileError`` occurs during the execution of the
statements in the ``try`` block, the corresponding ``except`` block is
executed.
Line 9
An ``OSError`` could be triggered here.
Line 12
Here you trigger the ``EmptyFileError``.
Line 17
The ``else`` clause is optional; it is executed if no exception occurs in
the ``try`` block.
Line 19
The ``finally`` clause is also optional and is executed at the end of the
block, regardless of whether an exception was triggered or not.

.. note::
The way Python handles error situations in general differs from some other
languages, such as Java. These languages check possible errors as far as
possible before they occur, as handling exceptions after they occur is
costly. This is sometimes referred to as the :abbr:`LBYL (Look before you
leap)` approach.

Python, on the other hand, relies more on exceptions to handle errors after
they occur. Although this reliance may seem risky, when exceptions are used
correctly, the code is less cumbersome and easier to read, and errors are
only handled when they occur. This Pythonic approach to error handling is
often described as :abbr:`EAFP (easier to ask forgiveness than permission)`.

Checks
------

* Write code that receives two numbers and divides the first number by the
second. Check if the :class:`python3:ZeroDivisionError` occurs when the second
number is ``0`` and catch it.

* If :class:`MyError` inherits from :class:`Exception`, what is the difference
between ``except Exception as e`` and ``except MyError as e``?

* Write a simple program that receives a number and then uses the :func:`assert`
statement to throw an :class:`exception <python3:Exception>` if the number is
``0``.

* Writes a user-defined exception :class:`Outliers` that throws an exception if
the variable ``x`` is greater or less than ``3``?

* Is the check whether an object is a list (:ref:`Check: list <check-list>`)
programming in the style of :abbr:`LBYL (look before you leap)` or
:abbr:`EAFP (easier to ask forgiveness than permission)`?
32 changes: 32 additions & 0 deletions docs/control-flow/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Control flow
============

Python has a whole range of structures for controlling code execution and
programme flow, including common branches and loops:

:doc:`boolean`
check values and identity and allow links between the two.
:doc:`conditional`
execute the code block after the first true condition of an ``if`` or
``elif`` statement; if neither condition is true, the code block is executed
after the ``else``.
:doc:`loops`
While ``while`` loops are executed as long as the condition is true, ``for``
loops iterate over :doc:`../types/sequences-sets/lists`,
:doc:`../types/sequences-sets/tuples` and
:doc:`../types/sequences-sets/sets`.
:doc:`exceptions`
usually deal with errors that occur during the execution of programmes.
:doc:`with`
regulates access to files, the locking of threads and the suppression of
:doc:`exceptions`, among other things.

.. toctree::
:titlesonly:
:hidden:

boolean
conditional
loops
exceptions
with
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
66 changes: 0 additions & 66 deletions docs/control-flows/boolean.rst

This file was deleted.

Loading

0 comments on commit 3a95a8d

Please sign in to comment.