diff --git a/05_numbers.ipynb b/05_numbers.ipynb
index ccb1412..df7598c 100644
--- a/05_numbers.ipynb
+++ b/05_numbers.ipynb
@@ -92,7 +92,7 @@
{
"data": {
"text/plain": [
- "139927641322704"
+ "140218620396752"
]
},
"execution_count": 2,
@@ -2115,7 +2115,7 @@
{
"data": {
"text/plain": [
- "139927641500360"
+ "140218620574432"
]
},
"execution_count": 63,
@@ -3985,7 +3985,7 @@
}
},
"source": [
- "As the exact implementation of floats may vary and be dependent on a particular Python installation, we look up the [float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/) to check the details. Usually, this is not necessary."
+ "As the exact implementation of floats may vary and be dependent on a particular Python installation, we look up the [float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/index.html) to check the details. Usually, this is not necessary."
]
},
{
@@ -4044,7 +4044,7 @@
}
},
"source": [
- "The [decimal](https://docs.python.org/3/library/decimal.html) module in the standard library provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n",
+ "The [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n",
"\n",
"We import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module."
]
@@ -4460,7 +4460,7 @@
}
},
"source": [
- "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/) should only be used with `float` objects as they do *not* preserve precision."
+ "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) should only be used with `float` objects as they do *not* preserve precision."
]
},
{
@@ -4903,7 +4903,7 @@
}
},
"source": [
- "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n",
+ "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/index.html). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n",
"\n",
"We import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module."
]
@@ -5411,7 +5411,7 @@
{
"data": {
"text/plain": [
- "139927632267248"
+ "140218611341648"
]
},
"execution_count": 173,
@@ -6002,7 +6002,898 @@
}
},
"source": [
- "The [cmath](https://docs.python.org/3/library/cmath.html) module in the [standard library](https://docs.python.org/3/library/) implements many of the functions from the [math](https://docs.python.org/3/library/math.html) module such that they work with complex numbers."
+ "The [cmath](https://docs.python.org/3/library/cmath.html) module in the [standard library](https://docs.python.org/3/library/index.html) implements many of the functions from the [math](https://docs.python.org/3/library/math.html) module such that they work with complex numbers."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "## The Numeric Tower"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n",
+ "\n",
+ "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n",
+ "\n",
+ "- $\\mathbb{N}$: [Natural numbers](https://en.wikipedia.org/wiki/Natural_number) are all non-negative count numbers, e.g., $0, 1, 2, ...$\n",
+ "- $\\mathbb{Z}$: [Integers](https://en.wikipedia.org/wiki/Integer) are all numbers *without* a fractional component, e.g., $-1, 0, 1, ...$\n",
+ "- $\\mathbb{Q}$: [Rational numbers](https://en.wikipedia.org/wiki/Rational_number) are all numbers that can be expressed as a quotient of two integers, e.g., $-\\frac{1}{2}, 0, \\frac{1}{2}, ...$\n",
+ "- $\\mathbb{R}$: [Real numbers](https://en.wikipedia.org/wiki/Real_number) are all numbers that can be represented as a distance along a line (negative means \"reversed\"), e.g., $\\sqrt{2}, \\pi, \\text{e}, ...$\n",
+ "- $\\mathbb{C}$: [Complex numbers](https://en.wikipedia.org/wiki/Complex_number) are all numbers of the form $a + b\\textbf{i}$ where $a$ and $b$ are real numbers and $\\textbf{i}$ is the [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number), e.g., $0, \\textbf{i}, 1 + \\textbf{i}, ...$\n",
+ "\n",
+ "In the listed order, the five sets are perfect subsets and $\\mathbb{C}$ is the largest set (to be precise, all sets are infinite, but they still have a different number of elements)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The *concrete* data types introduced in this chapter are all *imperfect* models of *abstract* mathematical ideas.\n",
+ "\n",
+ "The `int` and `Fraction` types are the models \"closest\" to the idea they implement: Whereas $\\mathbb{Z}$ and $\\mathbb{Q}$ are, by definition, infinite, every computer runs out of bits when representing sufficiently large integers or fractions with a sufficiently large number of decimals. However, within a system-dependent minimum and maximum integer range, we can model an integer or fraction without any loss in precision.\n",
+ "\n",
+ "For the other types, in particular, the `float` type, the implications of their imprecision are discussed in detail above.\n",
+ "\n",
+ "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what Pythonistas call the **numeric tower**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python slang:\n",
+ "\n",
+ "- `numbers.Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n",
+ "- `numbers.Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n",
+ "- `numbers.Real`: \"all real numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))\n",
+ "- `numbers.Rational`: \"all rational numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Rational))\n",
+ "- `numbers.Integral`: \"all integers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Integral))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 195,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import numbers"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 196,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['ABCMeta',\n",
+ " 'Complex',\n",
+ " 'Integral',\n",
+ " 'Number',\n",
+ " 'Rational',\n",
+ " 'Real',\n",
+ " '__all__',\n",
+ " '__builtins__',\n",
+ " '__cached__',\n",
+ " '__doc__',\n",
+ " '__file__',\n",
+ " '__loader__',\n",
+ " '__name__',\n",
+ " '__package__',\n",
+ " '__spec__',\n",
+ " 'abstractmethod']"
+ ]
+ },
+ "execution_count": 196,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dir(numbers)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "As a reminder, the built-in [help()](https://docs.python.org/3/library/functions.html#help) function is always our friend.\n",
+ "\n",
+ "The abstract types' docstrings are unsurprisingly similar to the corresponding concrete types' docstrings (for now, let's not worry about the dunder-style names in the docstrings).\n",
+ "\n",
+ "For example, both `numbers.Complex` and `complex` list the `imag` and `real` attributes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 197,
+ "metadata": {
+ "scrolled": true,
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on class Complex in module numbers:\n",
+ "\n",
+ "class Complex(Number)\n",
+ " | Complex defines the operations that work on the builtin complex type.\n",
+ " | \n",
+ " | In short, those are: a conversion to complex, .real, .imag, +, -,\n",
+ " | *, /, abs(), .conjugate, ==, and !=.\n",
+ " | \n",
+ " | If it is given heterogeneous arguments, and doesn't have special\n",
+ " | knowledge about them, it should fall back to the builtin complex\n",
+ " | type as described below.\n",
+ " | \n",
+ " | Method resolution order:\n",
+ " | Complex\n",
+ " | Number\n",
+ " | builtins.object\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __abs__(self)\n",
+ " | Returns the Real distance from 0. Called for abs(self).\n",
+ " | \n",
+ " | __add__(self, other)\n",
+ " | self + other\n",
+ " | \n",
+ " | __bool__(self)\n",
+ " | True if self != 0. Called for bool(self).\n",
+ " | \n",
+ " | __complex__(self)\n",
+ " | Return a builtin complex instance. Called for complex(self).\n",
+ " | \n",
+ " | __eq__(self, other)\n",
+ " | self == other\n",
+ " | \n",
+ " | __mul__(self, other)\n",
+ " | self * other\n",
+ " | \n",
+ " | __neg__(self)\n",
+ " | -self\n",
+ " | \n",
+ " | __pos__(self)\n",
+ " | +self\n",
+ " | \n",
+ " | __pow__(self, exponent)\n",
+ " | self**exponent; should promote to float or complex when necessary.\n",
+ " | \n",
+ " | __radd__(self, other)\n",
+ " | other + self\n",
+ " | \n",
+ " | __rmul__(self, other)\n",
+ " | other * self\n",
+ " | \n",
+ " | __rpow__(self, base)\n",
+ " | base ** self\n",
+ " | \n",
+ " | __rsub__(self, other)\n",
+ " | other - self\n",
+ " | \n",
+ " | __rtruediv__(self, other)\n",
+ " | other / self\n",
+ " | \n",
+ " | __sub__(self, other)\n",
+ " | self - other\n",
+ " | \n",
+ " | __truediv__(self, other)\n",
+ " | self / other: Should promote to float when necessary.\n",
+ " | \n",
+ " | conjugate(self)\n",
+ " | (x+y*i).conjugate() returns (x-y*i).\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | imag\n",
+ " | Retrieve the imaginary component of this number.\n",
+ " | \n",
+ " | This should subclass Real.\n",
+ " | \n",
+ " | real\n",
+ " | Retrieve the real component of this number.\n",
+ " | \n",
+ " | This should subclass Real.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data and other attributes defined here:\n",
+ " | \n",
+ " | __abstractmethods__ = frozenset({'__abs__', '__add__', '__complex__', ...\n",
+ " | \n",
+ " | __hash__ = None\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "help(numbers.Complex)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "For sure, Python understands the built-in types as literals."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 198,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Help on class complex in module builtins:\n",
+ "\n",
+ "class complex(object)\n",
+ " | complex(real=0, imag=0)\n",
+ " | \n",
+ " | Create a complex number from a real part and an optional imaginary part.\n",
+ " | \n",
+ " | This is equivalent to (real + imag*1j) where imag defaults to 0.\n",
+ " | \n",
+ " | Methods defined here:\n",
+ " | \n",
+ " | __abs__(self, /)\n",
+ " | abs(self)\n",
+ " | \n",
+ " | __add__(self, value, /)\n",
+ " | Return self+value.\n",
+ " | \n",
+ " | __bool__(self, /)\n",
+ " | self != 0\n",
+ " | \n",
+ " | __divmod__(self, value, /)\n",
+ " | Return divmod(self, value).\n",
+ " | \n",
+ " | __eq__(self, value, /)\n",
+ " | Return self==value.\n",
+ " | \n",
+ " | __float__(self, /)\n",
+ " | float(self)\n",
+ " | \n",
+ " | __floordiv__(self, value, /)\n",
+ " | Return self//value.\n",
+ " | \n",
+ " | __format__(...)\n",
+ " | complex.__format__() -> str\n",
+ " | \n",
+ " | Convert to a string according to format_spec.\n",
+ " | \n",
+ " | __ge__(self, value, /)\n",
+ " | Return self>=value.\n",
+ " | \n",
+ " | __getattribute__(self, name, /)\n",
+ " | Return getattr(self, name).\n",
+ " | \n",
+ " | __getnewargs__(...)\n",
+ " | \n",
+ " | __gt__(self, value, /)\n",
+ " | Return self>value.\n",
+ " | \n",
+ " | __hash__(self, /)\n",
+ " | Return hash(self).\n",
+ " | \n",
+ " | __int__(self, /)\n",
+ " | int(self)\n",
+ " | \n",
+ " | __le__(self, value, /)\n",
+ " | Return self<=value.\n",
+ " | \n",
+ " | __lt__(self, value, /)\n",
+ " | Return self complex\n",
+ " | \n",
+ " | Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Static methods defined here:\n",
+ " | \n",
+ " | __new__(*args, **kwargs) from builtins.type\n",
+ " | Create and return a new object. See help(type) for accurate signature.\n",
+ " | \n",
+ " | ----------------------------------------------------------------------\n",
+ " | Data descriptors defined here:\n",
+ " | \n",
+ " | imag\n",
+ " | the imaginary part of a complex number\n",
+ " | \n",
+ " | real\n",
+ " | the real part of a complex number\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "help(complex)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "One way to use *abstract* data types is to use them in place of a *concrete* data type.\n",
+ "\n",
+ "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 199,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 199,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(1 / 10, float)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 200,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 200,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(1 / 10, numbers.Number)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 201,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 201,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(1 / 10, numbers.Complex)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 202,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 202,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(1 / 10, numbers.Real)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Due to the `float` type's inherent imprecision, `1 / 10` is *not* a rational number."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 203,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "False"
+ ]
+ },
+ "execution_count": 203,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(1 / 10, numbers.Rational)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "The `Fraction` type qualifies as a rational number; however, the `Decimal` type does not."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 204,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 204,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(Fraction(1, 10), numbers.Rational)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 205,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "False"
+ ]
+ },
+ "execution_count": 205,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "isinstance(Decimal(\"0.1\"), numbers.Rational)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "source": [
+ "#### Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to *take advantage of duck typing*. If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numeric type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 206,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import math"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 207,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0"
+ ]
+ },
+ "execution_count": 207,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "math.trunc(1 / 10)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 208,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def factorial(n, *, strict=True):\n",
+ " \"\"\"Calculate the factorial of a number.\n",
+ "\n",
+ " Args:\n",
+ " n (int): number to calculate the factorial for; must be positive\n",
+ " strict (bool): if n must not contain decimals; defaults to True\n",
+ "\n",
+ " Returns:\n",
+ " factorial (int)\n",
+ "\n",
+ " Raises:\n",
+ " TypeError: if n is not an integer or cannot be cast as such\n",
+ " ValueError: if n is negative\n",
+ " \"\"\"\n",
+ " if not isinstance(n, numbers.Integral):\n",
+ " if isinstance(n, numbers.Real):\n",
+ " if n != math.trunc(n) and strict:\n",
+ " raise ValueError(\"n is not an integer-like value; it has decimals\")\n",
+ " n = math.trunc(n)\n",
+ " else:\n",
+ " raise TypeError(\"Factorial is only defined for integers\")\n",
+ "\n",
+ " if n < 0:\n",
+ " raise ValueError(\"Factorial is not defined for negative integers\")\n",
+ " elif n == 0:\n",
+ " return 1\n",
+ " return n * factorial(n - 1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "`factorial()` works as before, but now also accepts, for example, `float` numbers."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 209,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1"
+ ]
+ },
+ "execution_count": 209,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "factorial(0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 210,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "-"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "6"
+ ]
+ },
+ "execution_count": 210,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "factorial(3)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 211,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "6"
+ ]
+ },
+ "execution_count": 211,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "factorial(3.0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may be rounded. By default, this is not allowed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 212,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "ename": "ValueError",
+ "evalue": "n is not an integer-like value; it has decimals",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mValueError\u001b[0m: n is not an integer-like value; it has decimals"
+ ]
+ }
+ ],
+ "source": [
+ "factorial(3.1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 213,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "fragment"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "6"
+ ]
+ },
+ "execution_count": 213,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "factorial(3.1, strict=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "slideshow": {
+ "slide_type": "skip"
+ }
+ },
+ "source": [
+ "For `complex` numbers, `factorial()` still raises a `TypeError`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 214,
+ "metadata": {
+ "slideshow": {
+ "slide_type": "slide"
+ }
+ },
+ "outputs": [
+ {
+ "ename": "TypeError",
+ "evalue": "Factorial is only defined for integers",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers"
+ ]
+ }
+ ],
+ "source": [
+ "factorial(1 + 2j)"
]
},
{
@@ -6029,14 +6920,16 @@
"- `float`: the \"gold\" standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n",
"- `complex`: layer on top of the `float` type; therefore inherently imprecise\n",
"\n",
- "Furthermore, the [standard library](https://docs.python.org/3/library/) adds two more types that can be used as substitutes for `float` objects:\n",
+ "Furthermore, the [standard library](https://docs.python.org/3/library/index.html) adds two more types that can be used as substitutes for `float` objects:\n",
"- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n",
"- `Fraction`: a perfect model for rational numbers (i.e., the set $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n",
"\n",
"The important takeaways for the data science practicioner are:\n",
"\n",
"1. **Do not mix** precise and imprecise data types, and\n",
- "2. actively expect `nan` results when working with `float` numbers as there are no **loud failures**."
+ "2. actively expect `nan` results when working with `float` numbers as there are no **loud failures**.\n",
+ "\n",
+ "The **numeric tower** is Python's way of implementing the various **abstract** ideas of what a number is in mathematics."
]
}
],
diff --git a/static/numbers.png b/static/numbers.png
new file mode 100644
index 0000000..6d75726
Binary files /dev/null and b/static/numbers.png differ