Add initial version of chapter 11, part 4

This commit is contained in:
Alexander Hess 2020-10-28 16:18:15 +01:00
parent 8dc67c5bdd
commit 85b7fd274c
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
9 changed files with 2927 additions and 222 deletions

View file

@ -1571,7 +1571,7 @@
} }
}, },
"source": [ "source": [
"Packages are a generalization of modules, and we look at one in detail in [Chapter 11 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). You may, however, already look at a [sample package <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#Packages-vs.-Modules).\n",
"\n", "\n",
"As a further reading on modules and packages, we refer to the [official tutorial <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/tutorial/modules.html)." "As a further reading on modules and packages, we refer to the [official tutorial <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/tutorial/modules.html)."
] ]

View file

@ -808,7 +808,7 @@
} }
}, },
"source": [ "source": [
"Every instance comes with a special `.__class__` attribute that also references the corresponding class object." "`v`'s semantic \"value\" is not so clear yet. We fix this in the next section."
] ]
}, },
{ {
@ -819,41 +819,6 @@
"slide_type": "fragment" "slide_type": "fragment"
} }
}, },
"outputs": [
{
"data": {
"text/plain": [
"__main__.Vector"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"v.__class__"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`v`'s semantic \"value\" is not so clear yet. We fix this in the next section."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@ -861,7 +826,7 @@
"<__main__.Vector at 0x7f9b9416d760>" "<__main__.Vector at 0x7f9b9416d760>"
] ]
}, },
"execution_count": 22, "execution_count": 21,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -885,7 +850,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 23, "execution_count": 22,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -910,7 +875,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 24, "execution_count": 23,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -946,7 +911,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 25, "execution_count": 24,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -983,7 +948,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 26, "execution_count": 25,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -996,7 +961,7 @@
"[1.0, 2.0, 3.0]" "[1.0, 2.0, 3.0]"
] ]
}, },
"execution_count": 26, "execution_count": 25,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1031,7 +996,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 27, "execution_count": 26,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1042,6 +1007,30 @@
"x = 1, 2, 3" "x = 1, 2, 3"
] ]
}, },
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"(1, 2, 3)"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 28, "execution_count": 28,
@ -1062,30 +1051,6 @@
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [
"x"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"(1, 2, 3)"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [ "source": [
"(1, 2, 3)" "(1, 2, 3)"
] ]
@ -1103,7 +1068,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 30, "execution_count": 29,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1144,7 +1109,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 31, "execution_count": 30,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1157,7 +1122,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 32, "execution_count": 31,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1170,7 +1135,7 @@
"Vector((1.000, 2.000, 3.000))" "Vector((1.000, 2.000, 3.000))"
] ]
}, },
"execution_count": 32, "execution_count": 31,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1192,7 +1157,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 33, "execution_count": 32,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "skip" "slide_type": "skip"
@ -1205,7 +1170,7 @@
"Vector((1.000, 2.000, 3.000))" "Vector((1.000, 2.000, 3.000))"
] ]
}, },
"execution_count": 33, "execution_count": 32,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1227,7 +1192,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 34, "execution_count": 33,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1240,7 +1205,7 @@
"'Vector((1.000, 2.000, 3.000))'" "'Vector((1.000, 2.000, 3.000))'"
] ]
}, },
"execution_count": 34, "execution_count": 33,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1266,7 +1231,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 35, "execution_count": 34,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1279,7 +1244,7 @@
"'Vector(1.0, ..., 3.0)[3]'" "'Vector(1.0, ..., 3.0)[3]'"
] ]
}, },
"execution_count": 35, "execution_count": 34,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1301,7 +1266,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 36, "execution_count": 35,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1342,14 +1307,14 @@
} }
}, },
"source": [ "source": [
"Below is a first implementation of the `Matrix` class that stores the entries internally as a `list` of `list`s.\n", "Below is a first implementation of the `Matrix` class that stores the `._entries` internally as a `list` of `list`s.\n",
"\n", "\n",
"The `.__init__()` method ensures that all the rows come with the same number of columns. Again, we do not allow `Matrix` instances without any entries." "The `.__init__()` method ensures that all the rows come with the same number of columns. Again, we do not allow `Matrix` instances without any entries."
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 37, "execution_count": 36,
"metadata": { "metadata": {
"code_folding": [], "code_folding": [],
"slideshow": { "slideshow": {
@ -1391,7 +1356,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 38, "execution_count": 37,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "skip" "slide_type": "skip"
@ -1404,7 +1369,7 @@
"94113690738160" "94113690738160"
] ]
}, },
"execution_count": 38, "execution_count": 37,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1413,6 +1378,30 @@
"id(Matrix)" "id(Matrix)"
] ]
}, },
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"type"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(Matrix)"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 39, "execution_count": 39,
@ -1421,30 +1410,6 @@
"slide_type": "skip" "slide_type": "skip"
} }
}, },
"outputs": [
{
"data": {
"text/plain": [
"type"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(Matrix)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@ -1452,7 +1417,7 @@
"__main__.Matrix" "__main__.Matrix"
] ]
}, },
"execution_count": 40, "execution_count": 39,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1474,7 +1439,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 41, "execution_count": 40,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1487,7 +1452,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 42, "execution_count": 41,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "skip" "slide_type": "skip"
@ -1500,7 +1465,7 @@
"140306180401856" "140306180401856"
] ]
}, },
"execution_count": 42, "execution_count": 41,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1511,7 +1476,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 43, "execution_count": 42,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1524,7 +1489,7 @@
"__main__.Matrix" "__main__.Matrix"
] ]
}, },
"execution_count": 43, "execution_count": 42,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1546,7 +1511,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 44, "execution_count": 43,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1559,7 +1524,7 @@
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
] ]
}, },
"execution_count": 44, "execution_count": 43,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1570,7 +1535,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 45, "execution_count": 44,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1602,7 +1567,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 46, "execution_count": 45,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "skip" "slide_type": "skip"
@ -1628,7 +1593,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 47, "execution_count": 46,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1673,12 +1638,14 @@
"source": [ "source": [
"The methods we have seen so far are all **instance methods**. The characteristic idea behind an instance method is that the behavior it provides either depends on the state of a concrete instance or mutates it. In other words, an instance method *always* works with attributes on the `self` argument. If a method does *not* need access to `self` to do its job, it is conceptually *not* an instance method and we should probably convert it into another kind of method as shown below.\n", "The methods we have seen so far are all **instance methods**. The characteristic idea behind an instance method is that the behavior it provides either depends on the state of a concrete instance or mutates it. In other words, an instance method *always* works with attributes on the `self` argument. If a method does *not* need access to `self` to do its job, it is conceptually *not* an instance method and we should probably convert it into another kind of method as shown below.\n",
"\n", "\n",
"An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Vector`'s rows and columns. The built-in [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries`. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking." "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Matrix`'s rows and columns. The built-in [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries` attribute. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking.\n",
"\n",
"Also, we see that it is ok to reference a class from within one of its methods. While this seems trivial to some readers, others may find this confusing. The final versions of the `Vector` and `Matrix` classes in the [fourth part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#The-final-Vector-and-Matrix-Classes) of this chapter show how this \"hard coded\" redundancy can be avoided."
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 48, "execution_count": 47,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1713,7 +1680,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 49, "execution_count": 48,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1724,6 +1691,30 @@
"m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])"
] ]
}, },
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 50, "execution_count": 50,
@ -1732,30 +1723,6 @@
"slide_type": "fragment" "slide_type": "fragment"
} }
}, },
"outputs": [
{
"data": {
"text/plain": [
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@ -1763,7 +1730,7 @@
"Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))"
] ]
}, },
"execution_count": 51, "execution_count": 50,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -1785,7 +1752,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 52, "execution_count": 51,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1796,6 +1763,30 @@
"n = m.transpose().transpose()" "n = m.transpose().transpose()"
] ]
}, },
{
"cell_type": "code",
"execution_count": 52,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
]
},
"execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 53, "execution_count": 53,
@ -1808,7 +1799,7 @@
{ {
"data": { "data": {
"text/plain": [ "text/plain": [
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" "False"
] ]
}, },
"execution_count": 53, "execution_count": 53,
@ -1817,7 +1808,18 @@
} }
], ],
"source": [ "source": [
"n" "m is n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter."
] ]
}, },
{ {
@ -1840,41 +1842,6 @@
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [
"m is n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter."
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [ "source": [
"m == n" "m == n"
] ]
@ -1894,7 +1861,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 56, "execution_count": 55,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1933,7 +1900,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 57, "execution_count": 56,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -1946,7 +1913,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 58, "execution_count": 57,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -1959,7 +1926,7 @@
"Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))"
] ]
}, },
"execution_count": 58, "execution_count": 57,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -2007,7 +1974,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 59, "execution_count": 58,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -2051,7 +2018,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 60, "execution_count": 59,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "slide" "slide_type": "slide"
@ -2064,7 +2031,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 61, "execution_count": 60,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"
@ -2077,7 +2044,7 @@
"(2, 3)" "(2, 3)"
] ]
}, },
"execution_count": 61, "execution_count": 60,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@ -2099,7 +2066,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 62, "execution_count": 61,
"metadata": { "metadata": {
"slideshow": { "slideshow": {
"slide_type": "fragment" "slide_type": "fragment"

View file

@ -1577,7 +1577,7 @@
} }
}, },
"source": [ "source": [
"After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable classes in the rest of this chapter." "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable implementations in the rest of this chapter. To lower the chance that we accidently design parts of our classes to be mutable, we replace the built-in [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) constructor with [tuple() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-tuple) in the `.__init__()` methods. As we learn in [Chapter 7 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Tuples-are-like-%22Immutable-Lists%22), `tuple`s are like immutable `list`s."
] ]
}, },
{ {

View file

@ -82,7 +82,7 @@
"class Vector:\n", "class Vector:\n",
"\n", "\n",
" def __init__(self, data):\n", " def __init__(self, data):\n",
" self._entries = list(float(x) for x in data)\n", " self._entries = tuple(float(x) for x in data)\n",
" # ...\n", " # ...\n",
"\n", "\n",
" def __repr__(self):\n", " def __repr__(self):\n",
@ -111,7 +111,7 @@
"class Matrix:\n", "class Matrix:\n",
"\n", "\n",
" def __init__(self, data):\n", " def __init__(self, data):\n",
" self._entries = list(list(float(x) for x in r) for r in data)\n", " self._entries = tuple(tuple(float(x) for x in r) for r in data)\n",
" # ...\n", " # ...\n",
"\n", "\n",
" def __repr__(self):\n", " def __repr__(self):\n",
@ -678,7 +678,7 @@
"class Vector:\n", "class Vector:\n",
"\n", "\n",
" def __init__(self, data):\n", " def __init__(self, data):\n",
" self._entries = list(float(x) for x in data)\n", " self._entries = tuple(float(x) for x in data)\n",
" # ...\n", " # ...\n",
"\n", "\n",
" def __repr__(self):\n", " def __repr__(self):\n",
@ -692,7 +692,7 @@
" return iter(self._entries)\n", " return iter(self._entries)\n",
"\n", "\n",
" def __add__(self, other):\n", " def __add__(self, other):\n",
" if isinstance(other, self.__class__): # vector addition\n", " if isinstance(other, Vector): # vector addition\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" return Vector(x + y for (x, y) in zip(self, other))\n", " return Vector(x + y for (x, y) in zip(self, other))\n",
@ -704,7 +704,7 @@
" return self + other\n", " return self + other\n",
"\n", "\n",
" def __sub__(self, other):\n", " def __sub__(self, other):\n",
" if isinstance(other, self.__class__): # vector subtraction\n", " if isinstance(other, Vector): # vector subtraction\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" return Vector(x - y for (x, y) in zip(self, other))\n", " return Vector(x - y for (x, y) in zip(self, other))\n",
@ -719,7 +719,7 @@
" return NotImplemented\n", " return NotImplemented\n",
"\n", "\n",
" def __mul__(self, other):\n", " def __mul__(self, other):\n",
" if isinstance(other, self.__class__): # dot product\n", " if isinstance(other, Vector): # dot product\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" return sum(x * y for (x, y) in zip(self, other))\n", " return sum(x * y for (x, y) in zip(self, other))\n",
@ -1142,7 +1142,7 @@
"class Matrix:\n", "class Matrix:\n",
"\n", "\n",
" def __init__(self, data):\n", " def __init__(self, data):\n",
" self._entries = list(list(float(x) for x in r) for r in data)\n", " self._entries = tuple(tuple(float(x) for x in r) for r in data)\n",
" # ...\n", " # ...\n",
"\n", "\n",
" def __repr__(self):\n", " def __repr__(self):\n",
@ -1169,7 +1169,7 @@
" return (self._entries[r][c] for r in range(self.n_rows) for c in range(self.n_cols))\n", " return (self._entries[r][c] for r in range(self.n_rows) for c in range(self.n_cols))\n",
"\n", "\n",
" def __add__(self, other):\n", " def __add__(self, other):\n",
" if isinstance(other, self.__class__): # matrix addition\n", " if isinstance(other, Matrix): # matrix addition\n",
" if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n",
" raise ValueError(\"matrices must have the same dimensions\")\n", " raise ValueError(\"matrices must have the same dimensions\")\n",
" return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n", " return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n",
@ -1184,7 +1184,7 @@
" return self + other\n", " return self + other\n",
"\n", "\n",
" def __sub__(self, other):\n", " def __sub__(self, other):\n",
" if isinstance(other, self.__class__): # matrix subtraction\n", " if isinstance(other, Matrix): # matrix subtraction\n",
" if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n",
" raise ValueError(\"matrices must have the same dimensions\")\n", " raise ValueError(\"matrices must have the same dimensions\")\n",
" return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n", " return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n",
@ -1211,7 +1211,7 @@
" return Matrix((x * other for x in r) for r in self._entries)\n", " return Matrix((x * other for x in r) for r in self._entries)\n",
" elif isinstance(other, Vector):\n", " elif isinstance(other, Vector):\n",
" return self._matrix_multiply(other.as_matrix()).as_vector()\n", " return self._matrix_multiply(other.as_matrix()).as_vector()\n",
" elif isinstance(other, self.__class__):\n", " elif isinstance(other, Matrix):\n",
" return self._matrix_multiply(other)\n", " return self._matrix_multiply(other)\n",
" return NotImplemented\n", " return NotImplemented\n",
"\n", "\n",
@ -1720,7 +1720,7 @@
" zero_threshold = 1e-12\n", " zero_threshold = 1e-12\n",
"\n", "\n",
" def __init__(self, data):\n", " def __init__(self, data):\n",
" self._entries = list(float(x) for x in data)\n", " self._entries = tuple(float(x) for x in data)\n",
" # ...\n", " # ...\n",
"\n", "\n",
" def __repr__(self):\n", " def __repr__(self):\n",
@ -1734,7 +1734,7 @@
" return iter(self._entries)\n", " return iter(self._entries)\n",
"\n", "\n",
" def __eq__(self, other):\n", " def __eq__(self, other):\n",
" if isinstance(other, self.__class__):\n", " if isinstance(other, Vector):\n",
" if len(self) != len(other):\n", " if len(self) != len(other):\n",
" raise ValueError(\"vectors must be of the same length\")\n", " raise ValueError(\"vectors must be of the same length\")\n",
" for x, y in zip(self, other):\n", " for x, y in zip(self, other):\n",
@ -1990,7 +1990,7 @@
"class Vector:\n", "class Vector:\n",
"\n", "\n",
" def __init__(self, data):\n", " def __init__(self, data):\n",
" self._entries = list(float(x) for x in data)\n", " self._entries = tuple(float(x) for x in data)\n",
" # ...\n", " # ...\n",
"\n", "\n",
" def __repr__(self):\n", " def __repr__(self):\n",

2719
11_classes/04_content.ipynb Normal file

File diff suppressed because one or more lines are too long

View file

@ -28,3 +28,7 @@ from sample_package.vector import Vector
__name__ = "linear_algebra_tools" __name__ = "linear_algebra_tools"
__version__ = "0.1.0" # see https://semver.org/ for how the format works __version__ = "0.1.0" # see https://semver.org/ for how the format works
__author__ = "Alexander Hess" __author__ = "Alexander Hess"
# Define what is imported with the "star import"
# (i.e., with `from sample_package import *`).
__all__ = ["Matrix", "Vector"]

View file

@ -17,12 +17,14 @@ class Matrix:
defaults to tuple defaults to tuple
typing (callable): type casting applied to all entries upon creation; typing (callable): type casting applied to all entries upon creation;
defaults to float defaults to float
vector_cls (vector.Vector): a reference to the Vector class to work with
zero_threshold (float): max. tolerance when comparing an entry to zero; zero_threshold (float): max. tolerance when comparing an entry to zero;
defaults to 1e-12 defaults to 1e-12
""" """
storage = utils.DEFAULT_ENTRIES_STORAGE storage = utils.DEFAULT_ENTRIES_STORAGE
typing = utils.DEFAULT_ENTRY_TYPE typing = utils.DEFAULT_ENTRY_TYPE
# the `vector_cls` attribute is set at the bottom of this file
zero_threshold = utils.ZERO_THRESHOLD zero_threshold = utils.ZERO_THRESHOLD
def __init__(self, data): def __init__(self, data):
@ -156,7 +158,7 @@ class Matrix:
Returns: Returns:
rows (generator): produces a Matrix's rows as Vectors rows (generator): produces a Matrix's rows as Vectors
""" """
return (Vector(r) for r in self._entries) return (self.vector_cls(r) for r in self._entries)
def cols(self): def cols(self):
"""Loop over a Matrix's columns. """Loop over a Matrix's columns.
@ -165,7 +167,7 @@ class Matrix:
columns (generator): produces a Matrix's columns as Vectors columns (generator): produces a Matrix's columns as Vectors
""" """
return ( return (
Vector(self._entries[r][c] for r in range(self.n_rows)) self.vector_cls(self._entries[r][c] for r in range(self.n_rows))
for c in range(self.n_cols) for c in range(self.n_cols)
) )
@ -232,7 +234,7 @@ class Matrix:
def __radd__(self, other): def __radd__(self, other):
"""See docstring for .__add__().""" """See docstring for .__add__()."""
if isinstance(other, Vector): if isinstance(other, self.vector_cls):
raise TypeError("vectors and matrices cannot be added") raise TypeError("vectors and matrices cannot be added")
# As both matrix and broadcasting addition are commutative, # As both matrix and broadcasting addition are commutative,
# we dispatch to .__add__(). # we dispatch to .__add__().
@ -260,7 +262,7 @@ class Matrix:
def __rsub__(self, other): def __rsub__(self, other):
"""See docstring for .__sub__().""" """See docstring for .__sub__()."""
if isinstance(other, Vector): if isinstance(other, self.vector_cls):
raise TypeError("vectors and matrices cannot be subtracted") raise TypeError("vectors and matrices cannot be subtracted")
# Same comments as in .__sub__() apply # Same comments as in .__sub__() apply
# with the roles of self and other swapped. # with the roles of self and other swapped.
@ -294,6 +296,8 @@ class Matrix:
Matrix-vector and vector-matrix multiplication are not commutative. Matrix-vector and vector-matrix multiplication are not commutative.
>>> from sample_package import Vector
>>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6]) >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])
Vector((17.000, 39.000)) Vector((17.000, 39.000))
@ -304,7 +308,7 @@ class Matrix:
if isinstance(other, numbers.Number): if isinstance(other, numbers.Number):
return self.__class__((x * other for x in r) for r in self._entries) return self.__class__((x * other for x in r) for r in self._entries)
# Matrix-vector multiplication: Vector is a column Vector # Matrix-vector multiplication: Vector is a column Vector
elif isinstance(other, Vector): elif isinstance(other, self.vector_cls):
# First, cast the other Vector as a Matrix, then do matrix-matrix # First, cast the other Vector as a Matrix, then do matrix-matrix
# multiplication, and lastly return the result as a Vector again. # multiplication, and lastly return the result as a Vector again.
return self._matrix_multiply(other.as_matrix()).as_vector() return self._matrix_multiply(other.as_matrix()).as_vector()
@ -319,7 +323,7 @@ class Matrix:
if isinstance(other, numbers.Number): if isinstance(other, numbers.Number):
return self * other return self * other
# Vector-matrix multiplication: Vector is a row Vector # Vector-matrix multiplication: Vector is a row Vector
elif isinstance(other, Vector): elif isinstance(other, self.vector_cls):
return other.as_matrix(column=False)._matrix_multiply(self).as_vector() return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
return NotImplemented return NotImplemented
@ -375,7 +379,7 @@ class Matrix:
def __abs__(self): def __abs__(self):
"""The Frobenius norm of a Matrix.""" """The Frobenius norm of a Matrix."""
return utils.norm(self) # use the norm() function shared with the Vector class return utils.norm(self) # uses the norm() function shared vector.Vector
def __bool__(self): def __bool__(self):
"""A Matrix is truthy if its Frobenius norm is strictly positive.""" """A Matrix is truthy if its Frobenius norm is strictly positive."""
@ -398,7 +402,7 @@ class Matrix:
"""Get a Vector representation of a Matrix. """Get a Vector representation of a Matrix.
Returns: Returns:
vector (Vector) vector (vector.Vector)
Raises: Raises:
RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1 RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1
@ -409,7 +413,7 @@ class Matrix:
""" """
if not (self.n_rows == 1 or self.n_cols == 1): if not (self.n_rows == 1 or self.n_cols == 1):
raise RuntimeError("one dimension (m or n) must be 1") raise RuntimeError("one dimension (m or n) must be 1")
return Vector(x for x in self) return self.vector_cls(x for x in self)
def transpose(self): def transpose(self):
"""Switch the rows and columns of a Matrix. """Switch the rows and columns of a Matrix.
@ -432,4 +436,8 @@ class Matrix:
# We call that a circular import. Whereas Python handles "circular" references # We call that a circular import. Whereas Python handles "circular" references
# (e.g., both the Matrix and Vector classes have methods that reference the # (e.g., both the Matrix and Vector classes have methods that reference the
# respective other class), that is forbidden for imports. # respective other class), that is forbidden for imports.
from sample_package.vector import Vector from sample_package import vector
# This attribute cannot be set in the class definition
# as the vector module is only imported down here.
Matrix.vector_cls = vector.Vector

View file

@ -7,8 +7,8 @@ import numbers
# If third-party libraries are needed, they are # If third-party libraries are needed, they are
# put into a group on their own in between. # put into a group on their own in between.
# Within a group, imports are sorted lexicographically. # Within a group, imports are sorted lexicographically.
from sample_package import matrix
from sample_package import utils from sample_package import utils
from sample_package.matrix import Matrix
class Vector: class Vector:
@ -17,6 +17,7 @@ class Vector:
All entries are converted to floats, or whatever is set in the typing attribute. All entries are converted to floats, or whatever is set in the typing attribute.
Attributes: Attributes:
matrix_cls (matrix.Matrix): a reference to the Matrix class to work with
storage (callable): data type used to store the entries internally; storage (callable): data type used to store the entries internally;
defaults to tuple defaults to tuple
typing (callable): type casting applied to all entries upon creation; typing (callable): type casting applied to all entries upon creation;
@ -25,6 +26,7 @@ class Vector:
defaults to 1e-12 defaults to 1e-12
""" """
matrix_cls = matrix.Matrix
storage = utils.DEFAULT_ENTRIES_STORAGE storage = utils.DEFAULT_ENTRIES_STORAGE
typing = utils.DEFAULT_ENTRY_TYPE typing = utils.DEFAULT_ENTRY_TYPE
zero_threshold = utils.ZERO_THRESHOLD zero_threshold = utils.ZERO_THRESHOLD
@ -219,7 +221,7 @@ class Vector:
def __abs__(self): def __abs__(self):
"""The Euclidean norm of a vector.""" """The Euclidean norm of a vector."""
return utils.norm(self) # use the norm() function shared with the Matrix class return utils.norm(self) # uses the norm() function shared matrix.Matrix
def __bool__(self): def __bool__(self):
"""A Vector is truthy if its Euclidean norm is strictly positive.""" """A Vector is truthy if its Euclidean norm is strictly positive."""
@ -246,7 +248,7 @@ class Vector:
column vector or a row vector; defaults to True column vector or a row vector; defaults to True
Returns: Returns:
matrix (Matrix) matrix (matrix.Matrix)
Example Usage: Example Usage:
>>> v = Vector([1, 2, 3]) >>> v = Vector([1, 2, 3])
@ -256,5 +258,5 @@ class Vector:
Matrix(((1.000, 2.000, 3.000,))) Matrix(((1.000, 2.000, 3.000,)))
""" """
if column: if column:
return Matrix([x] for x in self) return self.matrix_cls([x] for x in self)
return Matrix([(x for x in self)]) return self.matrix_cls([(x for x in self)])

View file

@ -284,3 +284,8 @@ If this is not possible,
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb) [<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb)
(Operator Overloading: Arithmetic & Relational Operators; (Operator Overloading: Arithmetic & Relational Operators;
Number Emulation) Number Emulation)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb)
(Writing one's own Packages;
The final `Vector` & `Matrix` Classes;
Comparison with `numpy`)