Merge branch 'chapter-11-classes' into develop

This commit is contained in:
Alexander Hess 2020-10-28 23:10:40 +01:00
commit 73432b9060
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
19 changed files with 12276 additions and 28 deletions

View file

@ -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 --

View file

@ -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)"
] ]
}, },
{ {

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)."
] ]

2146
11_classes/00_content.ipynb Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

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

File diff suppressed because one or more lines are too long

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
View 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
View 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
}

View 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"]

View 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

View 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))

View 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)])

View file

@ -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)

View file

@ -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

View file

@ -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
View file

@ -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"},
]

View file

@ -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"