Add section on the numeric tower

This commit is contained in:
Alexander Hess 2019-10-14 18:27:42 +02:00
parent dbc3a67af4
commit 76350cf895
2 changed files with 903 additions and 10 deletions

View file

@ -92,7 +92,7 @@
{ {
"data": { "data": {
"text/plain": [ "text/plain": [
"139927641322704" "140218620396752"
] ]
}, },
"execution_count": 2, "execution_count": 2,
@ -2115,7 +2115,7 @@
{ {
"data": { "data": {
"text/plain": [ "text/plain": [
"139927641500360" "140218620574432"
] ]
}, },
"execution_count": 63, "execution_count": 63,
@ -3985,7 +3985,7 @@
} }
}, },
"source": [ "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": [ "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", "\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." "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": [ "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": [ "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", "\n",
"We import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module." "We import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module."
] ]
@ -5411,7 +5411,7 @@
{ {
"data": { "data": {
"text/plain": [ "text/plain": [
"139927632267248" "140218611341648"
] ]
}, },
"execution_count": 173, "execution_count": 173,
@ -6002,7 +6002,898 @@
} }
}, },
"source": [ "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": [
"<img src=\"static/numbers.png\" width=\"75%\" align=\"center\">"
]
},
{
"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<value.\n",
" | \n",
" | __mod__(self, value, /)\n",
" | Return self%value.\n",
" | \n",
" | __mul__(self, value, /)\n",
" | Return self*value.\n",
" | \n",
" | __ne__(self, value, /)\n",
" | Return self!=value.\n",
" | \n",
" | __neg__(self, /)\n",
" | -self\n",
" | \n",
" | __pos__(self, /)\n",
" | +self\n",
" | \n",
" | __pow__(self, value, mod=None, /)\n",
" | Return pow(self, value, mod).\n",
" | \n",
" | __radd__(self, value, /)\n",
" | Return value+self.\n",
" | \n",
" | __rdivmod__(self, value, /)\n",
" | Return divmod(value, self).\n",
" | \n",
" | __repr__(self, /)\n",
" | Return repr(self).\n",
" | \n",
" | __rfloordiv__(self, value, /)\n",
" | Return value//self.\n",
" | \n",
" | __rmod__(self, value, /)\n",
" | Return value%self.\n",
" | \n",
" | __rmul__(self, value, /)\n",
" | Return value*self.\n",
" | \n",
" | __rpow__(self, value, mod=None, /)\n",
" | Return pow(value, self, mod).\n",
" | \n",
" | __rsub__(self, value, /)\n",
" | Return value-self.\n",
" | \n",
" | __rtruediv__(self, value, /)\n",
" | Return value/self.\n",
" | \n",
" | __str__(self, /)\n",
" | Return str(self).\n",
" | \n",
" | __sub__(self, value, /)\n",
" | Return self-value.\n",
" | \n",
" | __truediv__(self, value, /)\n",
" | Return self/value.\n",
" | \n",
" | conjugate(...)\n",
" | complex.conjugate() -> 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<ipython-input-212-188b816be8b6>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-208-65a2caa81b4a>\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<ipython-input-214-7296a74f5dcf>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-208-65a2caa81b4a>\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", "- `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", "- `complex`: layer on top of the `float` type; therefore inherently imprecise\n",
"\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", "- `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", "- `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", "\n",
"The important takeaways for the data science practicioner are:\n", "The important takeaways for the data science practicioner are:\n",
"\n", "\n",
"1. **Do not mix** precise and imprecise data types, and\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."
] ]
} }
], ],

BIN
static/numbers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB