Merge branch 'chapter-11-classes' into develop
This commit is contained in:
commit
73432b9060
19 changed files with 12276 additions and 28 deletions
|
@ -3,6 +3,12 @@ fail_fast: true
|
||||||
repos:
|
repos:
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
|
- id: doctests
|
||||||
|
name: Run the xdoctests in the source files
|
||||||
|
entry: poetry run nox -s doctests --
|
||||||
|
language: system
|
||||||
|
stages: [commit, merge-commit]
|
||||||
|
types: [python]
|
||||||
- id: fix-branch-references
|
- id: fix-branch-references
|
||||||
name: Check for wrong branch references
|
name: Check for wrong branch references
|
||||||
entry: poetry run nox -s fix-branch-references --
|
entry: poetry run nox -s fix-branch-references --
|
||||||
|
|
|
@ -775,7 +775,7 @@
|
||||||
" - *Chapter 9*: [Mappings & Sets <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)\n",
|
" - *Chapter 9*: [Mappings & Sets <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)\n",
|
||||||
" - *Chapter 10*: Arrays & Dataframes\n",
|
" - *Chapter 10*: Arrays & Dataframes\n",
|
||||||
"- How can we create custom data types?\n",
|
"- How can we create custom data types?\n",
|
||||||
" - *Chapter 11*: Classes & Instances"
|
" - *Chapter 11*: [Classes & Instances <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)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)."
|
||||||
]
|
]
|
||||||
|
|
2146
11_classes/00_content.ipynb
Normal file
2146
11_classes/00_content.ipynb
Normal file
File diff suppressed because it is too large
Load diff
1502
11_classes/01_exercises.ipynb
Normal file
1502
11_classes/01_exercises.ipynb
Normal file
File diff suppressed because it is too large
Load diff
1983
11_classes/02_content.ipynb
Normal file
1983
11_classes/02_content.ipynb
Normal file
File diff suppressed because it is too large
Load diff
2412
11_classes/03_content.ipynb
Normal file
2412
11_classes/03_content.ipynb
Normal file
File diff suppressed because one or more lines are too long
2985
11_classes/04_content.ipynb
Normal file
2985
11_classes/04_content.ipynb
Normal file
File diff suppressed because one or more lines are too long
100
11_classes/05_summary.ipynb
Normal file
100
11_classes/05_summary.ipynb
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "slide"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"# Chapter 11: Classes & Instances (TL;DR)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {
|
||||||
|
"slideshow": {
|
||||||
|
"slide_type": "skip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source": [
|
||||||
|
"With the `class` statement, we can create a *user-defined* data type that we also call a **class**.\n",
|
||||||
|
"\n",
|
||||||
|
"Then, to create new **instances** of the data type, we simply call the class, just as we do with the built-in constructors.\n",
|
||||||
|
"\n",
|
||||||
|
"Conceptually, a class is the **blueprint** defining the **behavior** each instance exhibits.\n",
|
||||||
|
"\n",
|
||||||
|
"In the example used throughout the chapter, the `Vector` and `Matrix` classes implement the linear algebra rules all `Vector` and `Matrix` instances follow *in general*. On the contrary, the instances **encapsulate** the **state** of *concrete* vectors and matrices.\n",
|
||||||
|
"\n",
|
||||||
|
"The `class` statement acts as a *namespace* that consists of simple *variable assignments* and *function definitions*:\n",
|
||||||
|
"1. Variables become the **class attributes** that are shared among all instances.\n",
|
||||||
|
"2. Functions may take a different role:\n",
|
||||||
|
" - By default, they become **instance methods** by going through a **binding process** where a reference to the instance on which the method is *invoked* is passed in as the first argument. By convention, the corresponding parameter is called `self`; it embodies an instance's state: That means that instance methods set and get **instance attributes** on and from `self`.\n",
|
||||||
|
" - They may be declared as **class methods**. Then, the binding process is adjusted such that the first argument passed in is a reference to the class itself and, by convention, named `cls`. A common use case is to design **alternative constructors**.\n",
|
||||||
|
" - They may also be declared as **properties**. A use case for that are *derived* attributes that follow semantically from an instance's state.\n",
|
||||||
|
" \n",
|
||||||
|
"The **Python Data Model** concerns what special methods (i.e., the ones with the dunder names) exists and how they work together.\n",
|
||||||
|
"\n",
|
||||||
|
"The instantiation process is controlled by the `.__init__()` method.\n",
|
||||||
|
"\n",
|
||||||
|
"The `__repr__()` and `__str__()` methods implement the **text representation** of an instance, which can be regarded as a Unicode encoded representation of all the state encapsulated in an instance.\n",
|
||||||
|
"\n",
|
||||||
|
"**Sequence emulation** means that a user-defined data type exhibits the same four properties as the built-in sequences, which are regarded as finite and iterable containers with a predictable order. The `.__len__()`, `.__iter__()`, `__reversed__()`, `__getitem__()`, and some others are used to implement the corresponding behaviors.\n",
|
||||||
|
"\n",
|
||||||
|
"Similarly, **number emulation** means that an instance of a user-defined data type behaves like a built-in number. For example, by implementing the `.__abs__()` method, an instance may be passed to the built-in [abs() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#abs) function.\n",
|
||||||
|
"\n",
|
||||||
|
"If different data types, built-in or user-defined, share a well-defined set of behaviors, a single function may be written to work with objects of all the data types. We describe such functions as **polymorphic**.\n",
|
||||||
|
"\n",
|
||||||
|
"Classes may specify how *operators* are **overloaded**. Examples for that are the `.__add__()`, `.__sub__()`, or `.__eq__()` methods.\n",
|
||||||
|
"\n",
|
||||||
|
"**Packages** are folders containing **modules** (i.e., \\*.py files) and a \"*\\_\\_init\\_\\_.py*\" file. We use them to design coherent libraries with reusable code."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.8.6"
|
||||||
|
},
|
||||||
|
"livereveal": {
|
||||||
|
"auto_select": "code",
|
||||||
|
"auto_select_fragment": true,
|
||||||
|
"scroll": true,
|
||||||
|
"theme": "serif"
|
||||||
|
},
|
||||||
|
"toc": {
|
||||||
|
"base_numbering": 1,
|
||||||
|
"nav_menu": {},
|
||||||
|
"number_sections": false,
|
||||||
|
"sideBar": true,
|
||||||
|
"skip_h1_title": true,
|
||||||
|
"title_cell": "Table of Contents",
|
||||||
|
"title_sidebar": "Contents",
|
||||||
|
"toc_cell": false,
|
||||||
|
"toc_position": {
|
||||||
|
"height": "calc(100% - 180px)",
|
||||||
|
"left": "10px",
|
||||||
|
"top": "150px",
|
||||||
|
"width": "384px"
|
||||||
|
},
|
||||||
|
"toc_section_display": false,
|
||||||
|
"toc_window_display": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 4
|
||||||
|
}
|
271
11_classes/06_review.ipynb
Normal file
271
11_classes/06_review.ipynb
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Chapter 11: Classes & Instances (Review Questions)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"The questions below assume that you have read the [first <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), [second <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/02_content.ipynb), [third <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/03_content.ipynb), and [fourth <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) part of Chapter 11.\n",
|
||||||
|
"\n",
|
||||||
|
"Be concise in your answers! Most questions can be answered in *one* sentence."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Essay Questions "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q1**: How are **classes** a way to manage the **state** in a big program? How should we think of classes conceptually?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q2**: What do we mean with **instantiation**? How do **instances** relate to **classes**?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q3:** What is an **implementation detail**? Name two different examples of implementation details regarding the `Vector` and `Matrix` classes!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q4**: How are **instance methods** different from **class methods**? How do **special methods** fit into the picture?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q5**: How do **mutability** and **immutability** come into play when designing a user-defined data type?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q6**: Explain the concept of **method chaining**!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q7**: How can we implement **operator overloading** for a user-defined data type? When do we need to user *reverse* special methods?"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## True / False Questions"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"Motivate your answer with *one short* sentence!"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q8**: An instance's **text representation** is a `bytes` object with a special encoding."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q9**: Computed **properties** are special kinds of **instance methods**."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q10**: **Sequence emulation** means designing a user-defined data type around the built-in `list` or `tuple` types."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q11**: The **Python Data Model** can be regarded as the \"Theory\" or \"Mental Model\" behind the Python language."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q12**: **Polymorphism** means that two instances have the same data type, be it a built-in or user-defined one."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q13**: **Number emulation** means that two instances of the same user-defined data type can be added together."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"**Q14**: **Packages** are a good place to collect all the code to be reused in a data science project, for example, across different Jupyter notebooks."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
" < your answer >"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.8.6"
|
||||||
|
},
|
||||||
|
"toc": {
|
||||||
|
"base_numbering": 1,
|
||||||
|
"nav_menu": {},
|
||||||
|
"number_sections": false,
|
||||||
|
"sideBar": true,
|
||||||
|
"skip_h1_title": true,
|
||||||
|
"title_cell": "Table of Contents",
|
||||||
|
"title_sidebar": "Contents",
|
||||||
|
"toc_cell": false,
|
||||||
|
"toc_position": {},
|
||||||
|
"toc_section_display": false,
|
||||||
|
"toc_window_display": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 4
|
||||||
|
}
|
34
11_classes/sample_package/__init__.py
Normal file
34
11_classes/sample_package/__init__.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
"""This package provides linear algebra functionalities.
|
||||||
|
|
||||||
|
The package is split into three modules:
|
||||||
|
- matrix: defines the Matrix class
|
||||||
|
- vector: defines the Vector class
|
||||||
|
- utils: defines the norm() function that is shared by Matrix and Vector
|
||||||
|
and package-wide constants
|
||||||
|
|
||||||
|
The classes implement arithmetic operations involving vectors and matrices.
|
||||||
|
|
||||||
|
See the docstrings in the modules and classes for further info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Import the classes here so that they are available
|
||||||
|
# from the package's top level. That means that a user
|
||||||
|
# who imports this package with `import sample_package`
|
||||||
|
# may then refer to, for example, the Matrix class with
|
||||||
|
# simply `sample_package.Matrix` instead of the longer
|
||||||
|
# `sample_package.matrix.Matrix`.
|
||||||
|
from sample_package.matrix import Matrix
|
||||||
|
from sample_package.vector import Vector
|
||||||
|
|
||||||
|
|
||||||
|
# Define meta information for the package.
|
||||||
|
# There are other (and more modern) ways of
|
||||||
|
# doing this, but specifying the following
|
||||||
|
# dunder variables here is the traditional way.
|
||||||
|
__name__ = "linear_algebra_tools"
|
||||||
|
__version__ = "0.1.0" # see https://semver.org/ for how the format works
|
||||||
|
__author__ = "Alexander Hess"
|
||||||
|
|
||||||
|
# Define what is imported with the "star import"
|
||||||
|
# (i.e., with `from sample_package import *`).
|
||||||
|
__all__ = ["Matrix", "Vector"]
|
443
11_classes/sample_package/matrix.py
Normal file
443
11_classes/sample_package/matrix.py
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
"""This module defines a Matrix class."""
|
||||||
|
|
||||||
|
import numbers
|
||||||
|
|
||||||
|
# Note the import at the bottom of this file, and
|
||||||
|
# see the comments about imports in the matrix module.
|
||||||
|
from sample_package import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Matrix:
|
||||||
|
"""An m-by-n-dimensional matrix from linear algebra.
|
||||||
|
|
||||||
|
All entries are converted to floats, or whatever is set in the typing attribute.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
storage (callable): data type used to store the entries internally;
|
||||||
|
defaults to tuple
|
||||||
|
typing (callable): type casting applied to all entries upon creation;
|
||||||
|
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;
|
||||||
|
defaults to 1e-12
|
||||||
|
"""
|
||||||
|
|
||||||
|
storage = utils.DEFAULT_ENTRIES_STORAGE
|
||||||
|
typing = utils.DEFAULT_ENTRY_TYPE
|
||||||
|
# the `vector_cls` attribute is set at the bottom of this file
|
||||||
|
zero_threshold = utils.ZERO_THRESHOLD
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
"""Create a new matrix.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (sequence of sequences): the matrix's entries;
|
||||||
|
viewed as a sequence of the matrix's rows (i.e., row-major order);
|
||||||
|
use the .from_columns() class method if the data come as a sequence
|
||||||
|
of the matrix's columns (i.e., column-major order)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError:
|
||||||
|
- if no entries are provided
|
||||||
|
- if the number of columns is inconsistent across the rows
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(1, 2), (3, 4)])
|
||||||
|
Matrix(((1.0, 2.0,), (3.0, 4.0,)))
|
||||||
|
"""
|
||||||
|
self._entries = self.storage(
|
||||||
|
self.storage(self.typing(x) for x in r) for r in data
|
||||||
|
)
|
||||||
|
for row in self._entries[1:]:
|
||||||
|
if len(row) != self.n_cols:
|
||||||
|
raise ValueError("rows must have the same number of entries")
|
||||||
|
if len(self) == 0:
|
||||||
|
raise ValueError("a matrix must have at least one entry")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_columns(cls, data):
|
||||||
|
"""Create a new matrix.
|
||||||
|
|
||||||
|
This is an alternative constructor for data provided in column-major order.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (sequence of sequences): the matrix's entries;
|
||||||
|
viewed as a sequence of the matrix's columns (i.e., column-major order);
|
||||||
|
use the normal constructor method if the data come as a sequence
|
||||||
|
of the matrix's rows (i.e., row-major order)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError:
|
||||||
|
- if no entries are provided
|
||||||
|
- if the number of rows is inconsistent across the columns
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix.from_columns([(1, 2), (3, 4)])
|
||||||
|
Matrix(((1.0, 3.0,), (2.0, 4.0,)))
|
||||||
|
"""
|
||||||
|
return cls(data).transpose()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_rows(cls, data):
|
||||||
|
"""See docstring for .__init__()."""
|
||||||
|
# Some users may want to use this .from_rows() constructor
|
||||||
|
# to explicitly communicate that the data are in row-major order.
|
||||||
|
# Otherwise, this method is redundant.
|
||||||
|
return cls(data)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Text representation of a Matrix."""
|
||||||
|
name = self.__class__.__name__
|
||||||
|
args = ", ".join(
|
||||||
|
"(" + ", ".join(repr(c) for c in r) + ",)" for r in self._entries
|
||||||
|
)
|
||||||
|
return f"{name}(({args}))"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Human-readable text representation of a Matrix."""
|
||||||
|
name = self.__class__.__name__
|
||||||
|
first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols
|
||||||
|
return f"{name}(({first!r}, ...), ..., (..., {last!r}))[{m:d}x{n:d}]"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def n_rows(self):
|
||||||
|
"""Number of rows in a Matrix."""
|
||||||
|
return len(self._entries)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def n_cols(self):
|
||||||
|
"""Number of columns in a Matrix."""
|
||||||
|
return len(self._entries[0])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Number of entries in a Matrix."""
|
||||||
|
return self.n_rows * self.n_cols
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
"""Obtain an individual entry of a Matrix.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index (int / tuple of int's): if index is an integer,
|
||||||
|
the Matrix is viewed as a sequence in row-major order;
|
||||||
|
if index is a tuple of integers, the first one refers to
|
||||||
|
the row and the second one to the column of the entry
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
entry (Matrix.typing)
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> m = Matrix([(1, 2), (3, 4)])
|
||||||
|
>>> m[0]
|
||||||
|
1.0
|
||||||
|
>>> m[-1]
|
||||||
|
4.0
|
||||||
|
>>> m[0, 1]
|
||||||
|
2.0
|
||||||
|
"""
|
||||||
|
# Sequence-like indexing (one-dimensional)
|
||||||
|
if isinstance(index, int):
|
||||||
|
if index < 0:
|
||||||
|
index += len(self)
|
||||||
|
if not (0 <= index < len(self)):
|
||||||
|
raise IndexError("integer index out of range")
|
||||||
|
row, col = divmod(index, self.n_cols)
|
||||||
|
return self._entries[row][col]
|
||||||
|
# Mathematical-like indexing (two-dimensional)
|
||||||
|
elif (
|
||||||
|
isinstance(index, tuple)
|
||||||
|
and len(index) == 2
|
||||||
|
and isinstance(index[0], int)
|
||||||
|
and isinstance(index[1], int)
|
||||||
|
):
|
||||||
|
return self._entries[index[0]][index[1]]
|
||||||
|
raise TypeError("index must be either an int or a tuple of two int's")
|
||||||
|
|
||||||
|
def rows(self):
|
||||||
|
"""Loop over a Matrix's rows.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
rows (generator): produces a Matrix's rows as Vectors
|
||||||
|
"""
|
||||||
|
return (self.vector_cls(r) for r in self._entries)
|
||||||
|
|
||||||
|
def cols(self):
|
||||||
|
"""Loop over a Matrix's columns.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
columns (generator): produces a Matrix's columns as Vectors
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
self.vector_cls(self._entries[r][c] for r in range(self.n_rows))
|
||||||
|
for c in range(self.n_cols)
|
||||||
|
)
|
||||||
|
|
||||||
|
def entries(self, *, reverse=False, row_major=True):
|
||||||
|
"""Loop over a Matrix's entries.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
reverse (bool): flag to loop backwards; defaults to False
|
||||||
|
row_major (bool): flag to loop in row-major order; defaults to True
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
entries (generator): produces a Matrix's entries
|
||||||
|
"""
|
||||||
|
if reverse:
|
||||||
|
rows = range(self.n_rows - 1, -1, -1)
|
||||||
|
cols = range(self.n_cols - 1, -1, -1)
|
||||||
|
else:
|
||||||
|
rows, cols = range(self.n_rows), range(self.n_cols)
|
||||||
|
if row_major:
|
||||||
|
return (self._entries[r][c] for r in rows for c in cols)
|
||||||
|
return (self._entries[r][c] for c in cols for r in rows)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Loop over a Matrix's entries.
|
||||||
|
|
||||||
|
See .entries() for more customization options.
|
||||||
|
"""
|
||||||
|
return self.entries()
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
"""Loop over a Matrix's entries in reverse order.
|
||||||
|
|
||||||
|
See .entries() for more customization options.
|
||||||
|
"""
|
||||||
|
return self.entries(reverse=True)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
"""Handle `self + other` and `other + self`.
|
||||||
|
|
||||||
|
This may be either matrix addition or broadcasting addition.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])
|
||||||
|
Matrix(((3.0, 5.0,), (7.0, 9.0,)))
|
||||||
|
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) + 5
|
||||||
|
Matrix(((6.0, 7.0,), (8.0, 9.0,)))
|
||||||
|
|
||||||
|
>>> 10 + Matrix([(1, 2), (3, 4)])
|
||||||
|
Matrix(((11.0, 12.0,), (13.0, 14.0,)))
|
||||||
|
"""
|
||||||
|
# Matrix addition
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
|
||||||
|
raise ValueError("matrices must have the same dimensions")
|
||||||
|
return self.__class__(
|
||||||
|
(s_col + o_col for (s_col, o_col) in zip(s_row, o_row))
|
||||||
|
for (s_row, o_row) in zip(self._entries, other._entries)
|
||||||
|
)
|
||||||
|
# Broadcasting addition
|
||||||
|
elif isinstance(other, numbers.Number):
|
||||||
|
return self.__class__((c + other for c in r) for r in self._entries)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
"""See docstring for .__add__()."""
|
||||||
|
if isinstance(other, self.vector_cls):
|
||||||
|
raise TypeError("vectors and matrices cannot be added")
|
||||||
|
# As both matrix and broadcasting addition are commutative,
|
||||||
|
# we dispatch to .__add__().
|
||||||
|
return self + other
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
"""Handle `self - other` and `other - self`.
|
||||||
|
|
||||||
|
This may be either matrix subtraction or broadcasting subtraction.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])
|
||||||
|
Matrix(((1.0, 1.0,), (1.0, 1.0,)))
|
||||||
|
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) - 1
|
||||||
|
Matrix(((0.0, 1.0,), (2.0, 3.0,)))
|
||||||
|
|
||||||
|
>>> 10 - Matrix([(1, 2), (3, 4)])
|
||||||
|
Matrix(((9.0, 8.0,), (7.0, 6.0,)))
|
||||||
|
"""
|
||||||
|
# As subtraction is the inverse of addition,
|
||||||
|
# we first dispatch to .__neg__() to invert the signs of
|
||||||
|
# all entries in other and then dispatch to .__add__().
|
||||||
|
return self + (-other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
"""See docstring for .__sub__()."""
|
||||||
|
if isinstance(other, self.vector_cls):
|
||||||
|
raise TypeError("vectors and matrices cannot be subtracted")
|
||||||
|
# Same comments as in .__sub__() apply
|
||||||
|
# with the roles of self and other swapped.
|
||||||
|
return (-self) + other
|
||||||
|
|
||||||
|
def _matrix_multiply(self, other):
|
||||||
|
"""Internal utility method to multiply to Matrix instances."""
|
||||||
|
if self.n_cols != other.n_rows:
|
||||||
|
raise ValueError("matrices must have compatible dimensions")
|
||||||
|
# Matrix-matrix multiplication means that each entry of the resulting
|
||||||
|
# Matrix is the dot product of the respective row of the "left" Matrix
|
||||||
|
# and column of the "right" Matrix. So, the rows/columns are represented
|
||||||
|
# by the Vector instances provided by the .cols() and .rows() methods.
|
||||||
|
return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows())
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
"""Handle `self * other` and `other * self`.
|
||||||
|
|
||||||
|
This may be either scalar multiplication, matrix-vector multiplication,
|
||||||
|
vector-matrix multiplication, or matrix-matrix multiplication.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])
|
||||||
|
Matrix(((7.0, 10.0,), (15.0, 22.0,)))
|
||||||
|
|
||||||
|
>>> 2 * Matrix([(1, 2), (3, 4)])
|
||||||
|
Matrix(((2.0, 4.0,), (6.0, 8.0,)))
|
||||||
|
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) * 3
|
||||||
|
Matrix(((3.0, 6.0,), (9.0, 12.0,)))
|
||||||
|
|
||||||
|
Matrix-vector and vector-matrix multiplication are not commutative.
|
||||||
|
|
||||||
|
>>> from sample_package import Vector
|
||||||
|
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])
|
||||||
|
Vector((17.0, 39.0))
|
||||||
|
|
||||||
|
>>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])
|
||||||
|
Vector((23.0, 34.0))
|
||||||
|
"""
|
||||||
|
# Scalar multiplication
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self.__class__((x * other for x in r) for r in self._entries)
|
||||||
|
# Matrix-vector multiplication: Vector is a column Vector
|
||||||
|
elif isinstance(other, self.vector_cls):
|
||||||
|
# First, cast the other Vector as a Matrix, then do matrix-matrix
|
||||||
|
# multiplication, and lastly return the result as a Vector again.
|
||||||
|
return self._matrix_multiply(other.as_matrix()).as_vector()
|
||||||
|
# Matrix-matrix multiplication
|
||||||
|
elif isinstance(other, self.__class__):
|
||||||
|
return self._matrix_multiply(other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
"""See docstring for .__mul__()."""
|
||||||
|
# As scalar multiplication is commutative, we dispatch to .__mul__().
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self * other
|
||||||
|
# Vector-matrix multiplication: Vector is a row Vector
|
||||||
|
elif isinstance(other, self.vector_cls):
|
||||||
|
return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
"""Handle `self / other`.
|
||||||
|
|
||||||
|
Divide a Matrix by a scalar.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) / 4
|
||||||
|
Matrix(((0.25, 0.5,), (0.75, 1.0,)))
|
||||||
|
"""
|
||||||
|
# As scalar division division is the same as multiplication
|
||||||
|
# with the inverse, we dispatch to .__mul__().
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self * (1 / other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Handle `self == other`.
|
||||||
|
|
||||||
|
Compare two Matrix instances for equality.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)])
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)])
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
|
||||||
|
raise ValueError("matrices must have the same dimensions")
|
||||||
|
for x, y in zip(self, other):
|
||||||
|
if abs(x - y) > self.zero_threshold:
|
||||||
|
return False # exit early if two corresponding entries differ
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
"""Handle `+self`.
|
||||||
|
|
||||||
|
This is simply an identity operator returning the Matrix itself.
|
||||||
|
"""
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
"""Handle `-self`.
|
||||||
|
|
||||||
|
Negate all entries of a Matrix.
|
||||||
|
"""
|
||||||
|
return self.__class__((-x for x in r) for r in self._entries)
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
"""The Frobenius norm of a Matrix."""
|
||||||
|
return utils.norm(self) # uses the norm() function shared vector.Vector
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
"""A Matrix is truthy if its Frobenius norm is strictly positive."""
|
||||||
|
return bool(abs(self))
|
||||||
|
|
||||||
|
def __float__(self):
|
||||||
|
"""Cast a Matrix as a scalar.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
scalar (float)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: if the Matrix has more than one entry
|
||||||
|
"""
|
||||||
|
if not (self.n_rows == 1 and self.n_cols == 1):
|
||||||
|
raise RuntimeError("matrix must have exactly one entry to become a scalar")
|
||||||
|
return self[0]
|
||||||
|
|
||||||
|
def as_vector(self):
|
||||||
|
"""Get a Vector representation of a Matrix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
vector (vector.Vector)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Matrix([(1, 2, 3)]).as_vector()
|
||||||
|
Vector((1.0, 2.0, 3.0))
|
||||||
|
"""
|
||||||
|
if not (self.n_rows == 1 or self.n_cols == 1):
|
||||||
|
raise RuntimeError("one dimension (m or n) must be 1")
|
||||||
|
return self.vector_cls(x for x in self)
|
||||||
|
|
||||||
|
def transpose(self):
|
||||||
|
"""Switch the rows and columns of a Matrix.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matrix (Matrix)
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> m = Matrix([(1, 2), (3, 4)])
|
||||||
|
>>> m
|
||||||
|
Matrix(((1.0, 2.0,), (3.0, 4.0,)))
|
||||||
|
>>> m.transpose()
|
||||||
|
Matrix(((1.0, 3.0,), (2.0, 4.0,)))
|
||||||
|
"""
|
||||||
|
return self.__class__(zip(*self._entries))
|
||||||
|
|
||||||
|
|
||||||
|
# This import needs to be made here as otherwise an ImportError is raised.
|
||||||
|
# That is so as both the matrix and vector modules import a class from each other.
|
||||||
|
# We call that a circular import. Whereas Python handles "circular" references
|
||||||
|
# (e.g., both the Matrix and Vector classes have methods that reference the
|
||||||
|
# respective other class), that is forbidden for imports.
|
||||||
|
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
|
35
11_classes/sample_package/utils.py
Normal file
35
11_classes/sample_package/utils.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
"""This module provides utilities for the whole package.
|
||||||
|
|
||||||
|
The defined constants are used as defaults in the Vector and Matrix classes.
|
||||||
|
|
||||||
|
The norm() function is shared by Vector.__abs__() and Matrix.__abs__().
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
# Define constants (i.e., normal variables that are, by convention, named in UPPERCASE)
|
||||||
|
# that are used as the defaults for class attributes within Vector and Matrix.
|
||||||
|
DEFAULT_ENTRIES_STORAGE = tuple
|
||||||
|
DEFAULT_ENTRY_TYPE = float
|
||||||
|
ZERO_THRESHOLD = 1e-12
|
||||||
|
|
||||||
|
|
||||||
|
def norm(vec_or_mat):
|
||||||
|
"""Calculate the Frobenius or Euclidean norm of a matrix or vector.
|
||||||
|
|
||||||
|
Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vec_or_mat (Vector / Matrix): object whose entries are squared and summed up
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
norm (float)
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
As Vector and Matrix objects are by design non-empty sequences,
|
||||||
|
norm() may be called, for example, with `[3, 4]` as the argument:
|
||||||
|
>>> norm([3, 4])
|
||||||
|
5.0
|
||||||
|
"""
|
||||||
|
return math.sqrt(sum(x ** 2 for x in vec_or_mat))
|
262
11_classes/sample_package/vector.py
Normal file
262
11_classes/sample_package/vector.py
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
"""This module defines a Vector class."""
|
||||||
|
|
||||||
|
# Imports from the standard library go first ...
|
||||||
|
import numbers
|
||||||
|
|
||||||
|
# ... and are followed by project-internal ones.
|
||||||
|
# If third-party libraries are needed, they are
|
||||||
|
# put into a group on their own in between.
|
||||||
|
# Within a group, imports are sorted lexicographically.
|
||||||
|
from sample_package import matrix
|
||||||
|
from sample_package import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Vector:
|
||||||
|
"""A one-dimensional vector from linear algebra.
|
||||||
|
|
||||||
|
All entries are converted to floats, or whatever is set in the typing attribute.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
matrix_cls (matrix.Matrix): a reference to the Matrix class to work with
|
||||||
|
storage (callable): data type used to store the entries internally;
|
||||||
|
defaults to tuple
|
||||||
|
typing (callable): type casting applied to all entries upon creation;
|
||||||
|
defaults to float
|
||||||
|
zero_threshold (float): max. tolerance when comparing an entry to zero;
|
||||||
|
defaults to 1e-12
|
||||||
|
"""
|
||||||
|
|
||||||
|
matrix_cls = matrix.Matrix
|
||||||
|
storage = utils.DEFAULT_ENTRIES_STORAGE
|
||||||
|
typing = utils.DEFAULT_ENTRY_TYPE
|
||||||
|
zero_threshold = utils.ZERO_THRESHOLD
|
||||||
|
|
||||||
|
def __init__(self, data):
|
||||||
|
"""Create a new vector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (sequence): the vector's entries
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: if no entries are provided
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Vector([1, 2, 3])
|
||||||
|
Vector((1.0, 2.0, 3.0))
|
||||||
|
|
||||||
|
>>> Vector(range(3))
|
||||||
|
Vector((0.0, 1.0, 2.0))
|
||||||
|
"""
|
||||||
|
self._entries = self.storage(self.typing(x) for x in data)
|
||||||
|
if len(self) == 0:
|
||||||
|
raise ValueError("a vector must have at least one entry")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Text representation of a Vector."""
|
||||||
|
name = self.__class__.__name__
|
||||||
|
args = ", ".join(repr(x) for x in self)
|
||||||
|
return f"{name}(({args}))"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Human-readable text representation of a Vector."""
|
||||||
|
name = self.__class__.__name__
|
||||||
|
first, last, n_entries = self[0], self[-1], len(self)
|
||||||
|
return f"{name}({first!r}, ..., {last!r})[{n_entries:d}]"
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Number of entries in a Vector."""
|
||||||
|
return len(self._entries)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
"""Obtain an individual entry of a Vector."""
|
||||||
|
if not isinstance(index, int):
|
||||||
|
raise TypeError("index must be an integer")
|
||||||
|
return self._entries[index]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Loop over a Vector's entries."""
|
||||||
|
return iter(self._entries)
|
||||||
|
|
||||||
|
def __reversed__(self):
|
||||||
|
"""Loop over a Vector's entries in reverse order."""
|
||||||
|
return reversed(self._entries)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
"""Handle `self + other` and `other + self`.
|
||||||
|
|
||||||
|
This may be either vector addition or broadcasting addition.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Vector([1, 2, 3]) + Vector([2, 3, 4])
|
||||||
|
Vector((3.0, 5.0, 7.0))
|
||||||
|
|
||||||
|
>>> Vector([1, 2, 3]) + 4
|
||||||
|
Vector((5.0, 6.0, 7.0))
|
||||||
|
|
||||||
|
>>> 10 + Vector([1, 2, 3])
|
||||||
|
Vector((11.0, 12.0, 13.0))
|
||||||
|
"""
|
||||||
|
# Vector addition
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise ValueError("vectors must be of the same length")
|
||||||
|
return self.__class__(x + y for (x, y) in zip(self, other))
|
||||||
|
# Broadcasting addition
|
||||||
|
elif isinstance(other, numbers.Number):
|
||||||
|
return self.__class__(x + other for x in self)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
"""See docstring for .__add__()."""
|
||||||
|
# As both vector and broadcasting addition are commutative,
|
||||||
|
# we dispatch to .__add__().
|
||||||
|
return self + other
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
"""Handle `self - other` and `other - self`.
|
||||||
|
|
||||||
|
This may be either vector subtraction or broadcasting subtraction.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Vector([7, 8, 9]) - Vector([1, 2, 3])
|
||||||
|
Vector((6.0, 6.0, 6.0))
|
||||||
|
|
||||||
|
>>> Vector([1, 2, 3]) - 1
|
||||||
|
Vector((0.0, 1.0, 2.0))
|
||||||
|
|
||||||
|
>>> 10 - Vector([1, 2, 3])
|
||||||
|
Vector((9.0, 8.0, 7.0))
|
||||||
|
"""
|
||||||
|
# As subtraction is the inverse of addition,
|
||||||
|
# we first dispatch to .__neg__() to invert the signs of
|
||||||
|
# all entries in other and then dispatch to .__add__().
|
||||||
|
return self + (-other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
"""See docstring for .__sub__()."""
|
||||||
|
# Same comments as in .__sub__() apply
|
||||||
|
# with the roles of self and other swapped.
|
||||||
|
return (-self) + other
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
"""Handle `self * other` and `other * self`.
|
||||||
|
|
||||||
|
This may be either the dot product of two vectors or scalar multiplication.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Vector([1, 2, 3]) * Vector([2, 3, 4])
|
||||||
|
20.0
|
||||||
|
|
||||||
|
>>> 2 * Vector([1, 2, 3])
|
||||||
|
Vector((2.0, 4.0, 6.0))
|
||||||
|
|
||||||
|
>>> Vector([1, 2, 3]) * 3
|
||||||
|
Vector((3.0, 6.0, 9.0))
|
||||||
|
"""
|
||||||
|
# Dot product
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise ValueError("vectors must be of the same length")
|
||||||
|
return sum(x * y for (x, y) in zip(self, other))
|
||||||
|
# Scalar multiplication
|
||||||
|
elif isinstance(other, numbers.Number):
|
||||||
|
return self.__class__(x * other for x in self)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
"""See docstring for .__mul__()."""
|
||||||
|
# As both dot product and scalar multiplication are commutative,
|
||||||
|
# we dispatch to .__mul__().
|
||||||
|
return self * other
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
"""Handle `self / other`.
|
||||||
|
|
||||||
|
Divide a Vector by a scalar.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Vector([9, 6, 12]) / 3
|
||||||
|
Vector((3.0, 2.0, 4.0))
|
||||||
|
"""
|
||||||
|
# As scalar division division is the same as multiplication
|
||||||
|
# with the inverse, we dispatch to .__mul__().
|
||||||
|
if isinstance(other, numbers.Number):
|
||||||
|
return self * (1 / other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Handle `self == other`.
|
||||||
|
|
||||||
|
Compare two Vectors for equality.
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> Vector([1, 2, 3]) == Vector([1, 2, 3])
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> Vector([1, 2, 3]) == Vector([4, 5, 6])
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise ValueError("vectors must be of the same length")
|
||||||
|
for x, y in zip(self, other):
|
||||||
|
if abs(x - y) > self.zero_threshold:
|
||||||
|
return False # exit early if two corresponding entries differ
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __pos__(self):
|
||||||
|
"""Handle `+self`.
|
||||||
|
|
||||||
|
This is simply an identity operator returning the Vector itself.
|
||||||
|
"""
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __neg__(self):
|
||||||
|
"""Handle `-self`.
|
||||||
|
|
||||||
|
Negate all entries of a Vector.
|
||||||
|
"""
|
||||||
|
return self.__class__(-x for x in self)
|
||||||
|
|
||||||
|
def __abs__(self):
|
||||||
|
"""The Euclidean norm of a vector."""
|
||||||
|
return utils.norm(self) # uses the norm() function shared matrix.Matrix
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
"""A Vector is truthy if its Euclidean norm is strictly positive."""
|
||||||
|
return bool(abs(self))
|
||||||
|
|
||||||
|
def __float__(self):
|
||||||
|
"""Cast a Vector as a scalar.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
scalar (float)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: if the Vector has more than one entry
|
||||||
|
"""
|
||||||
|
if len(self) != 1:
|
||||||
|
raise RuntimeError("vector must have exactly one entry to become a scalar")
|
||||||
|
return self[0]
|
||||||
|
|
||||||
|
def as_matrix(self, *, column=True):
|
||||||
|
"""Get a Matrix representation of a Vector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
column (bool): if the vector is interpreted as a
|
||||||
|
column vector or a row vector; defaults to True
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matrix (matrix.Matrix)
|
||||||
|
|
||||||
|
Example Usage:
|
||||||
|
>>> v = Vector([1, 2, 3])
|
||||||
|
>>> v.as_matrix()
|
||||||
|
Matrix(((1.0,), (2.0,), (3.0,)))
|
||||||
|
>>> v.as_matrix(column=False)
|
||||||
|
Matrix(((1.0, 2.0, 3.0,)))
|
||||||
|
"""
|
||||||
|
if column:
|
||||||
|
return self.matrix_cls([x] for x in self)
|
||||||
|
return self.matrix_cls([(x for x in self)])
|
29
CONTENTS.md
29
CONTENTS.md
|
@ -262,3 +262,32 @@ If this is not possible,
|
||||||
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb)
|
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb)
|
||||||
- [further resources <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb)
|
- [further resources <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.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/09_mappings/08_resources.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/09_mappings/08_resources.ipynb)
|
||||||
|
- *Chapter 11*: Classes & Instances
|
||||||
|
- [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/00_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/00_content.ipynb)
|
||||||
|
(`class` Statement;
|
||||||
|
Instantiation;
|
||||||
|
Text Representations;
|
||||||
|
Instance Methods vs. Class Methods;
|
||||||
|
Computed Properties)
|
||||||
|
- [exercises <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/01_exercises.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/01_exercises.ipynb)
|
||||||
|
(A Traveling Salesman Problem)
|
||||||
|
- [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/02_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/02_content.ipynb)
|
||||||
|
(Sequence Emulation & Iteration;
|
||||||
|
Python's Data Model;
|
||||||
|
(Im)mutable Data Types;
|
||||||
|
Method Chaining;
|
||||||
|
Polymorphism)
|
||||||
|
- [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/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;
|
||||||
|
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`)
|
||||||
|
- [summary <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/05_summary.ipynb)
|
||||||
|
- [review questions <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/06_review.ipynb)
|
||||||
|
|
|
@ -22,6 +22,7 @@ For a more *detailed version* with **clickable links**
|
||||||
- *Chapter 7*: Sequential Data
|
- *Chapter 7*: Sequential Data
|
||||||
- *Chapter 8*: Map, Filter, & Reduce
|
- *Chapter 8*: Map, Filter, & Reduce
|
||||||
- *Chapter 9*: Mappings & Sets
|
- *Chapter 9*: Mappings & Sets
|
||||||
|
- *Chapter 11*: Classes & Instances
|
||||||
|
|
||||||
|
|
||||||
#### Videos
|
#### Videos
|
||||||
|
|
14
noxfile.py
14
noxfile.py
|
@ -4,6 +4,8 @@ Nox provides the following tasks:
|
||||||
|
|
||||||
- "init-project": install the pre-commit hooks
|
- "init-project": install the pre-commit hooks
|
||||||
|
|
||||||
|
- "doctests": run the xdoctests in the source files
|
||||||
|
|
||||||
- "fix-branch-references": adjusts links with git branch references in
|
- "fix-branch-references": adjusts links with git branch references in
|
||||||
various files (e.g., Mardown or notebooks)
|
various files (e.g., Mardown or notebooks)
|
||||||
|
|
||||||
|
@ -22,6 +24,11 @@ import nox
|
||||||
|
|
||||||
REPOSITORY = "webartifex/intro-to-python"
|
REPOSITORY = "webartifex/intro-to-python"
|
||||||
|
|
||||||
|
SRC_LOCATIONS = (
|
||||||
|
"02_functions/sample_module.py",
|
||||||
|
"11_classes/sample_package",
|
||||||
|
)
|
||||||
|
|
||||||
# Use a unified .cache/ folder for all develop tools.
|
# Use a unified .cache/ folder for all develop tools.
|
||||||
nox.options.envdir = ".cache/nox"
|
nox.options.envdir = ".cache/nox"
|
||||||
|
|
||||||
|
@ -45,6 +52,13 @@ def init_project(session):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@nox.session(venv_backend="none")
|
||||||
|
def doctests(session):
|
||||||
|
"""Run the xdoctests in the source files."""
|
||||||
|
for location in SRC_LOCATIONS:
|
||||||
|
session.run("poetry", "run", "xdoctest", "--silent", location)
|
||||||
|
|
||||||
|
|
||||||
@nox.session(name="fix-branch-references", venv_backend="none")
|
@nox.session(name="fix-branch-references", venv_backend="none")
|
||||||
def fix_branch_references(_session):
|
def fix_branch_references(_session):
|
||||||
"""Change git branch references.
|
"""Change git branch references.
|
||||||
|
|
74
poetry.lock
generated
74
poetry.lock
generated
|
@ -267,7 +267,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
parso = ">=0.7.0,<0.8.0"
|
parso = ">=0.7.0,<0.8.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
qa = ["flake8 (3.7.9)"]
|
qa = ["flake8 (==3.7.9)"]
|
||||||
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"]
|
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -428,7 +428,7 @@ test = ["jupyter-contrib-core", "nose", "requests", "selenium", "mock"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jupyterlab"
|
name = "jupyterlab"
|
||||||
version = "2.2.8"
|
version = "2.2.9"
|
||||||
description = "The JupyterLab notebook server extension."
|
description = "The JupyterLab notebook server extension."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -547,11 +547,11 @@ testpath = "*"
|
||||||
traitlets = ">=4.2"
|
traitlets = ">=4.2"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
|
all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
|
||||||
docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
|
docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
|
||||||
serve = ["tornado (>=4.0)"]
|
serve = ["tornado (>=4.0)"]
|
||||||
test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (0.2.2)"]
|
test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)"]
|
||||||
webpdf = ["pyppeteer (0.2.2)"]
|
webpdf = ["pyppeteer (==0.2.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nbformat"
|
name = "nbformat"
|
||||||
|
@ -573,7 +573,7 @@ test = ["fastjsonschema", "testpath", "pytest", "pytest-cov"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nest-asyncio"
|
name = "nest-asyncio"
|
||||||
version = "1.4.1"
|
version = "1.4.2"
|
||||||
description = "Patch asyncio to allow nested event loops"
|
description = "Patch asyncio to allow nested event loops"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -654,11 +654,11 @@ six = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pandocfilters"
|
name = "pandocfilters"
|
||||||
version = "1.4.2"
|
version = "1.4.3"
|
||||||
description = "Utilities for writing pandoc filters in python"
|
description = "Utilities for writing pandoc filters in python"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parso"
|
name = "parso"
|
||||||
|
@ -754,7 +754,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
version = "2.7.1"
|
version = "2.7.2"
|
||||||
description = "Pygments is a syntax highlighting package written in Python."
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -835,18 +835,18 @@ urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rise"
|
name = "rise"
|
||||||
version = "5.6.1"
|
version = "5.7.0"
|
||||||
description = "Reveal.js - Jupyter/IPython Slideshow Extension"
|
description = "Reveal.js - Jupyter/IPython Slideshow Extension"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
notebook = ">=5.5.0"
|
notebook = ">=6.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "send2trash"
|
name = "send2trash"
|
||||||
|
@ -929,11 +929,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
brotli = ["brotlipy (>=0.6.0)"]
|
brotli = ["brotlipy (>=0.6.0)"]
|
||||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtualenv"
|
name = "virtualenv"
|
||||||
version = "20.0.35"
|
version = "20.1.0"
|
||||||
description = "Virtual Python Environment builder"
|
description = "Virtual Python Environment builder"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -965,10 +965,28 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xdoctest"
|
||||||
|
version = "0.15.0"
|
||||||
|
description = "A rewrite of the builtin doctest module"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
six = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
all = ["six", "pytest", "pytest-cov", "codecov", "scikit-build", "cmake", "ninja", "pybind11", "pygments", "colorama", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"]
|
||||||
|
colors = ["pygments", "colorama"]
|
||||||
|
jupyter = ["nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"]
|
||||||
|
optional = ["pygments", "colorama", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"]
|
||||||
|
tests = ["pytest", "pytest-cov", "codecov", "scikit-build", "cmake", "ninja", "pybind11", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "abc30be36880f8b203c72895f70f906ef451fca6b4e5113891c9487bc95e02e5"
|
content-hash = "0483228a1bbf92c52f5045db05de80b951dbd7302db756ca6f65cd56b482b814"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
appdirs = [
|
appdirs = [
|
||||||
|
@ -1157,8 +1175,8 @@ jupyter-nbextensions-configurator = [
|
||||||
{file = "jupyter_nbextensions_configurator-0.4.1.tar.gz", hash = "sha256:e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"},
|
{file = "jupyter_nbextensions_configurator-0.4.1.tar.gz", hash = "sha256:e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"},
|
||||||
]
|
]
|
||||||
jupyterlab = [
|
jupyterlab = [
|
||||||
{file = "jupyterlab-2.2.8-py3-none-any.whl", hash = "sha256:95d0509557881cfa8a5fcdf225f2fca46faf1bc52fc56a28e0b72fcc594c90ab"},
|
{file = "jupyterlab-2.2.9-py3-none-any.whl", hash = "sha256:59af02c26a15ec2d2862a15bc72e41ae304b406a0b0d3f4f705eeb7caf91902b"},
|
||||||
{file = "jupyterlab-2.2.8.tar.gz", hash = "sha256:c8377bee30504919c1e79949f9fe35443ab7f5c4be622c95307e8108410c8b8c"},
|
{file = "jupyterlab-2.2.9.tar.gz", hash = "sha256:3be8f8edea173753dd838c1b6d3bbcb6f5c801121f824a477025c1b6a1d33dc6"},
|
||||||
]
|
]
|
||||||
jupyterlab-pygments = [
|
jupyterlab-pygments = [
|
||||||
{file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"},
|
{file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"},
|
||||||
|
@ -1258,8 +1276,8 @@ nbformat = [
|
||||||
{file = "nbformat-5.0.8.tar.gz", hash = "sha256:f545b22138865bfbcc6b1ffe89ed5a2b8e2dc5d4fe876f2ca60d8e6f702a30f8"},
|
{file = "nbformat-5.0.8.tar.gz", hash = "sha256:f545b22138865bfbcc6b1ffe89ed5a2b8e2dc5d4fe876f2ca60d8e6f702a30f8"},
|
||||||
]
|
]
|
||||||
nest-asyncio = [
|
nest-asyncio = [
|
||||||
{file = "nest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:a4487c4f49f2d11a7bb89a512a6886b6a5045f47097f49815b2851aaa8599cf0"},
|
{file = "nest_asyncio-1.4.2-py3-none-any.whl", hash = "sha256:c2d3bdc76ba235a7ad215128afe31d74a320d25790c50cd94685ec5ea221b94d"},
|
||||||
{file = "nest_asyncio-1.4.1.tar.gz", hash = "sha256:b86c3193abda5b2eeccf8c79894bc71c680369a178f4b068514ac00720b14e01"},
|
{file = "nest_asyncio-1.4.2.tar.gz", hash = "sha256:c614fcfaca72b1f04778bc0e73f49c84500b3d045c49d149fc46f1566643c175"},
|
||||||
]
|
]
|
||||||
nodeenv = [
|
nodeenv = [
|
||||||
{file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
|
{file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
|
||||||
|
@ -1306,7 +1324,7 @@ packaging = [
|
||||||
{file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
|
{file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
|
||||||
]
|
]
|
||||||
pandocfilters = [
|
pandocfilters = [
|
||||||
{file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"},
|
{file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"},
|
||||||
]
|
]
|
||||||
parso = [
|
parso = [
|
||||||
{file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"},
|
{file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"},
|
||||||
|
@ -1345,8 +1363,8 @@ pycparser = [
|
||||||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||||
]
|
]
|
||||||
pygments = [
|
pygments = [
|
||||||
{file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"},
|
{file = "Pygments-2.7.2-py3-none-any.whl", hash = "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"},
|
||||||
{file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"},
|
{file = "Pygments-2.7.2.tar.gz", hash = "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"},
|
||||||
]
|
]
|
||||||
pyparsing = [
|
pyparsing = [
|
||||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||||
|
@ -1433,8 +1451,8 @@ requests = [
|
||||||
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
|
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
|
||||||
]
|
]
|
||||||
rise = [
|
rise = [
|
||||||
{file = "rise-5.6.1-py2.py3-none-any.whl", hash = "sha256:e9637ee5499ad7801474da53a2c830350a44b2192c2f113594e4426190e55ad4"},
|
{file = "rise-5.7.0-py2.py3-none-any.whl", hash = "sha256:b500c7c3f7b09c8194a66feeffd60ead6da0132d9fa18a2bb7352587778ef482"},
|
||||||
{file = "rise-5.6.1.tar.gz", hash = "sha256:1343f068d01adc4dd0226d9b278ce93fc92f365d827431a57e8d5679eb39f4d6"},
|
{file = "rise-5.7.0.tar.gz", hash = "sha256:6c00721189e0b457ca40ab4eb0abef8edbba6c71bc04d7f04ad813a214ddea74"},
|
||||||
]
|
]
|
||||||
send2trash = [
|
send2trash = [
|
||||||
{file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"},
|
{file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"},
|
||||||
|
@ -1476,8 +1494,8 @@ urllib3 = [
|
||||||
{file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"},
|
{file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"},
|
||||||
]
|
]
|
||||||
virtualenv = [
|
virtualenv = [
|
||||||
{file = "virtualenv-20.0.35-py2.py3-none-any.whl", hash = "sha256:0ebc633426d7468664067309842c81edab11ae97fcaf27e8ad7f5748c89b431b"},
|
{file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"},
|
||||||
{file = "virtualenv-20.0.35.tar.gz", hash = "sha256:2a72c80fa2ad8f4e2985c06e6fc12c3d60d060e410572f553c90619b0f6efaf3"},
|
{file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"},
|
||||||
]
|
]
|
||||||
wcwidth = [
|
wcwidth = [
|
||||||
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||||
|
@ -1487,3 +1505,7 @@ webencodings = [
|
||||||
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
|
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
|
||||||
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
|
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
|
||||||
]
|
]
|
||||||
|
xdoctest = [
|
||||||
|
{file = "xdoctest-0.15.0-py2.py3-none-any.whl", hash = "sha256:695ea04303a48cbb319709270d43f7bae7f3de3701aec73f09d90a216499992e"},
|
||||||
|
{file = "xdoctest-0.15.0.tar.gz", hash = "sha256:7f0a184d403b69b166ebec1aadb13c98c96c59101e974ae2e4db4c3a803ec371"},
|
||||||
|
]
|
||||||
|
|
|
@ -21,6 +21,9 @@ numpy = "^1.19.2"
|
||||||
nox = "^2020.8.22"
|
nox = "^2020.8.22"
|
||||||
pre-commit = "^2.7.1"
|
pre-commit = "^2.7.1"
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
xdoctest = "^0.15.0"
|
||||||
|
|
||||||
# Live coding during presentation mode
|
# Live coding during presentation mode
|
||||||
jupyter-contrib-nbextensions = "^0.5.1"
|
jupyter-contrib-nbextensions = "^0.5.1"
|
||||||
rise = "^5.6.1"
|
rise = "^5.6.1"
|
||||||
|
|
Loading…
Reference in a new issue