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