Merge in "02-functions"

Merge branch "02-functions" into "develop"

Summary of the merged in commits:
 * 4643927: Adjust content overview in README.md
 * 0eb4b70: Add review and exercises for notebook 02
 * 72db877: Add initial version of notebook 02
 * ec3097e: Add the sample_package folder to the project
 * c3fc760: Add sample_module.py to the project
 * 22a85bc: Streamline previous content
 * f606838: Add numpy to the dependencies
This commit is contained in:
Alexander Hess 2019-09-22 20:36:28 +02:00
commit 8e841274e6
15 changed files with 4727 additions and 307 deletions

View file

@ -19,7 +19,7 @@
} }
}, },
"source": [ "source": [
"This course is set up to be a *thorough* introduction to programming in [Python](https://www.python.org/).\n", "This book is set up to be a *thorough* introduction to programming in [Python](https://www.python.org/).\n",
"\n", "\n",
"It teaches the concepts behind and the syntax of the core Python language as defined by the [Python Software Foundation](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n", "It teaches the concepts behind and the syntax of the core Python language as defined by the [Python Software Foundation](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n",
"\n", "\n",
@ -58,10 +58,10 @@
"source": [ "source": [
"To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n", "To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n",
"\n", "\n",
"- a *solid* understanding of the **English language** (i.e., usage of *technical* terms with *narrow* and *distinct* meanings),\n", "- a *solid* understanding of the **English language**,\n",
"- knowledge of **basic mathematics** from high school (i.e., addition, subtraction, multiplication, division, and a little bit of calculus and statistics),\n", "- knowledge of **basic mathematics** from high school,\n",
"- the ability to **think conceptually** and **reason logically** (i.e., *not* just memorizing), and\n", "- the ability to **think conceptually** and **reason logically**, and\n",
"- the willingness to **invest 2-4 hours a day for a month** (cf., \"ABC\"-rule at the end)" "- the willingness to **invest 2-4 hours a day for a month**."
] ]
}, },
{ {
@ -83,7 +83,7 @@
} }
}, },
"source": [ "source": [
"The course's **main goal** is to **prepare** the student **for further studies** in the \"field\" of **data science**." "The **main goal** of this introduction is to **prepare** the student **for further studies** in the \"field\" of **data science**."
] ]
}, },
{ {
@ -94,7 +94,7 @@
} }
}, },
"source": [ "source": [
"This includes but is not limited to more advanced courses on topics such as:\n", "This includes but is not limited to topics such as:\n",
"- linear algebra\n", "- linear algebra\n",
"- statistics & econometrics\n", "- statistics & econometrics\n",
"- data cleaning & wrangling\n", "- data cleaning & wrangling\n",
@ -154,7 +154,7 @@
} }
}, },
"source": [ "source": [
"To follow this course, a working installation of **Python 3.6** or higher is needed.\n", "To follow this book, a working installation of **Python 3.6** or higher is needed.\n",
"\n", "\n",
"A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n", "A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n",
"\n", "\n",
@ -180,7 +180,7 @@
} }
}, },
"source": [ "source": [
"To download the course's materials as a ZIP file, open the accompanying [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)." "To download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)."
] ]
}, },
{ {
@ -252,7 +252,7 @@
"\n", "\n",
"Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n", "Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n",
"\n", "\n",
"In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this course come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own." "In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own."
] ]
}, },
{ {
@ -295,7 +295,15 @@
"slide_type": "slide" "slide_type": "slide"
} }
}, },
"outputs": [], "outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello world\n"
]
}
],
"source": [ "source": [
"print(\"Hello world\")" "print(\"Hello world\")"
] ]
@ -351,7 +359,8 @@
} }
}, },
"source": [ "source": [
"For this course *programming* is \"defined\" as\n", "In this book *programming* is \"defined\" as:\n",
"\n",
"- a **structured** way of **problem solving**\n", "- a **structured** way of **problem solving**\n",
"- by **expressing** the steps of a **computation / process**\n", "- by **expressing** the steps of a **computation / process**\n",
"- and thereby **documenting** the process in a formal way\n", "- and thereby **documenting** the process in a formal way\n",
@ -369,7 +378,8 @@
} }
}, },
"source": [ "source": [
"That is different from *computer science*, which is\n", "That is different from *computer science*, which is:\n",
"\n",
"- a field of study comparable to (applied) **mathematics** that\n", "- a field of study comparable to (applied) **mathematics** that\n",
"- asks **abstract** questions (\"Is something computable at all?\"),\n", "- asks **abstract** questions (\"Is something computable at all?\"),\n",
"- develops and analyses **algorithms** and **data structures**,\n", "- develops and analyses **algorithms** and **data structures**,\n",
@ -419,6 +429,8 @@
} }
}, },
"source": [ "source": [
"Here is a brief history of and some background on Python:\n",
"\n",
"- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Pythons **[Benevolent Dictator for Life](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life)**) was bored during a week around Christmas 1989 and started Python as a hobby project \"that would keep \\[him\\] occupied\" for some days\n", "- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Pythons **[Benevolent Dictator for Life](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life)**) was bored during a week around Christmas 1989 and started Python as a hobby project \"that would keep \\[him\\] occupied\" for some days\n",
"- the idea was to create a **general-purpose scripting** language that would allow fast **prototyping** and would **run on every operating system**\n", "- the idea was to create a **general-purpose scripting** language that would allow fast **prototyping** and would **run on every operating system**\n",
"- Python grew through the 90s as van Rossum promoted it via his \"Computer Programming for Everybody\" initiative that had the **goal to encourage a basic level of coding literacy** as an equal knowledge alongside English literacy and math skills\n", "- Python grew through the 90s as van Rossum promoted it via his \"Computer Programming for Everybody\" initiative that had the **goal to encourage a basic level of coding literacy** as an equal knowledge alongside English literacy and math skills\n",
@ -427,17 +439,6 @@
"- the language is named after the sketch comedy group [Monty Python](https://en.wikipedia.org/wiki/Monty_Python)" "- the language is named after the sketch comedy group [Monty Python](https://en.wikipedia.org/wiki/Monty_Python)"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"#### Summary"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "metadata": {

View file

@ -18,7 +18,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Read chapter 0 of the book. Then work through the ten review questions." "Read Chapter 0 of the book. Then work through the ten review questions."
] ]
}, },
{ {

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Read chapter 1 of the book. Then work through the ten review questions." "Read Chapter 1 of the book. Then work through the ten review questions."
] ]
}, },
{ {
@ -180,7 +180,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **$8$ spaces** per level of indentation." "**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **8 spaces** per level of indentation."
] ]
}, },
{ {
@ -263,7 +263,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers $1$ through $10$ in just one line." "**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers 1 through 10 in just one line."
] ]
}, },
{ {
@ -298,7 +298,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q11.1**: First, create a list `numbers` with the numbers from $1$ through $100$. You could type all numbers manually but there is of course a smarter way. The built-in [range()](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." "**Q11.1**: First, create a list `numbers` with the numbers from 1 through 100. You could type all numbers manually but there is of course a smarter way. The built-in [range()](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)."
] ]
}, },
{ {
@ -335,7 +335,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with $100$ lines of output here." "**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here."
] ]
}, },
{ {

3478
02_functions.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,397 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 2: Functions & Modularization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Content Review"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Read Chapter 2 of the book. Then work through the ten review questions."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Essay Questions "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Answer the following questions briefly with *at most* 300 characters per question!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: What property of the `def` statement makes it a **statement**? Is there a way to use an **expression** to create a function?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: One of the first confusions of experienced programmers coming from other languages to Python regards the observation that **\"everything in Python is an object\"** (cf., this [discussion](https://www.reddit.com/r/learnpython/comments/8rypx9/everything_in_python_is_an_object/)). How does this relate to **functions**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: What does it mean for a variable to **go out of scope**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: How can a **global** variable be **shadowed**? Is this good or bad?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Explain the concept of **forwarding** a function **call**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: What are **keyword-only arguments** and when is it appropriate to use them?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"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": [
"**Q7**: A mere function **call** is just an **expression**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: When using the `import` statement, we need to ensure that the imported attributes do **not** overwrite any already defined variables and functions."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9:** Functions always have a name by which we can call them."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: The [standard library](https://docs.python.org/3/library/index.html) is a collection of numerical tools often used in scientific computing, for example, advanced mathematical functions or utilities for simulation."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Coding Exercises"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Volume of a Sphere"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11.1**: The [volume of a sphere](https://en.wikipedia.org/wiki/Sphere) is defined as $\\frac{4}{3} * \\pi * r^3$. Calculate this value for $r=10.0$ and round it to 10 digits after the comma. Use the [standard library](https://docs.python.org/3/library/index.html) to obtain a good approximation of $\\pi$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11.2**: Encapsulate the logic into a function `sphere_volume()` that takes one *positional* argument `radius` and one *keyword-only* argument `digits` defaulting to `5`. The volume should be returned as a `float` object under *all* circumstances. Document your work appropriately in a docstring according to [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11.3**: Evaluate the function with `radius = 100.0` and 1, 5, 10, 15, and 20 digits respectively."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11.4**: What observation do you make?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11.5**: Using the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in, write a `for`-loop and calculate the volume of a sphere with `radius = 42.0` for all `digits` from `1` through `20`. Print out each volume on a seperate line.\n",
"\n",
"Note: This is the first task where you actually need to use the [print()](https://docs.python.org/3/library/functions.html#print) built-in function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11.6**: What important lesson did you learn about the `float` type?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
}
],
"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.7.3"
},
"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": 2
}

View file

@ -14,6 +14,7 @@ As such they can be viewed in a plain web browser:
- [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) - [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb)
- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb) - [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb)
- [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)
However, it is recommended that students **install Python and Jupyter However, it is recommended that students **install Python and Jupyter
locally** and run the code in the notebooks on their own. locally** and run the code in the notebooks on their own.

11
poetry.lock generated
View file

@ -423,6 +423,14 @@ terminado = ">=0.8.1"
tornado = ">=5.0" tornado = ">=5.0"
traitlets = ">=4.2.1" traitlets = ">=4.2.1"
[[package]]
category = "main"
description = "NumPy is the fundamental package for array computing with Python."
name = "numpy"
optional = false
python-versions = ">=3.5"
version = "1.17.2"
[[package]] [[package]]
category = "main" category = "main"
description = "Utilities for writing pandoc filters in python" description = "Utilities for writing pandoc filters in python"
@ -673,7 +681,7 @@ version = "3.5.1"
notebook = ">=4.4.1" notebook = ">=4.4.1"
[metadata] [metadata]
content-hash = "431dace6d8d5c3e390b59e964116bb6219fb45c7a8c09cbfe99d70c704a54320" content-hash = "7c3d541c65a27324b49fc7ed2b84067e223eb4e20c20310ff9e485b016f65f91"
python-versions = "^3.6" python-versions = "^3.6"
[metadata.hashes] [metadata.hashes]
@ -711,6 +719,7 @@ mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "
nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"] nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"]
nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"] nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"]
notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"] notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"]
numpy = ["05dbfe72684cc14b92568de1bc1f41e5f62b00f714afc9adee42f6311738091f", "0d82cb7271a577529d07bbb05cb58675f2deb09772175fab96dc8de025d8ac05", "10132aa1fef99adc85a905d82e8497a580f83739837d7cbd234649f2e9b9dc58", "12322df2e21f033a60c80319c25011194cd2a21294cc66fee0908aeae2c27832", "16f19b3aa775dddc9814e02a46b8e6ae6a54ed8cf143962b4e53f0471dbd7b16", "3d0b0989dd2d066db006158de7220802899a1e5c8cf622abe2d0bd158fd01c2c", "438a3f0e7b681642898fd7993d38e2bf140a2d1eafaf3e89bb626db7f50db355", "5fd214f482ab53f2cea57414c5fb3e58895b17df6e6f5bca5be6a0bb6aea23bb", "73615d3edc84dd7c4aeb212fa3748fb83217e00d201875a47327f55363cef2df", "7bd355ad7496f4ce1d235e9814ec81ee3d28308d591c067ce92e49f745ba2c2f", "7d077f2976b8f3de08a0dcf5d72083f4af5411e8fddacd662aae27baa2601196", "a4092682778dc48093e8bda8d26ee8360153e2047826f95a3f5eae09f0ae3abf", "b458de8624c9f6034af492372eb2fee41a8e605f03f4732f43fc099e227858b2", "e70fc8ff03a961f13363c2c95ef8285e0cf6a720f8271836f852cc0fa64e97c8", "ee8e9d7cad5fe6dde50ede0d2e978d81eafeaa6233fb0b8719f60214cf226578", "f4a4f6aba148858a5a5d546a99280f71f5ee6ec8182a7d195af1a914195b21a2"]
pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"] pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"]
parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"]
pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"]

View file

@ -8,6 +8,7 @@ license = "MIT"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.6"
jupyter = "^1.0" jupyter = "^1.0"
numpy = "^1.17"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = {version = "^18.3-alpha.0", allows-prereleases = true} black = {version = "^18.3-alpha.0", allows-prereleases = true}

View file

@ -30,6 +30,7 @@ mistune==0.8.4
nbconvert==5.6.0 nbconvert==5.6.0
nbformat==4.4.0 nbformat==4.4.0
notebook==6.0.1 notebook==6.0.1
numpy==1.17.2
pandocfilters==1.4.2 pandocfilters==1.4.2
parso==0.5.1 parso==0.5.1
pexpect==4.7.0 pexpect==4.7.0

75
sample_module.py Normal file
View file

@ -0,0 +1,75 @@
"""This is a sample module.
It defines three functions average(), average_evens(), and average_odds().
The point is to show how we can put Python code in a .py file to be re-used
in some other place.
We should never forget to document the code as well, both on the module
level (i.e., this docstring) but also in every function it defines.
When imported, Python modules are executed top to bottom before the flow of
execution returns to wherever they were imported into.
An important convention is to prefix variables and functions that are not to
be used outside the module with a single underscore "_". This way, we can
design the code within a module in a modular fashion and only "export" what we
want.
Here, all three functions internally forward the computation to an internal
utility function _scaled_average() that contains all the logic common to the
three functions. Also, we define one _default_scalar variable that is used as
the default for the scalar parameter in each of the functions.
While this example is stylized, it shows how Python modules are often
designed.
"""
_default_scalar = 1
def _scaled_average(numbers, scalar):
"""Internal utility function to calculate scaled averages."""
average = sum(numbers) / len(numbers)
return scalar * average
def average(numbers, *, scalar=_default_scalar):
"""Calculate the average of all numbers in a list.
Args:
numbers (list): list of numbers; may be integers or floats
scalar (float, optional): the scalar that multiplies the
average of the even numbers
Returns:
float: (scaled) average
"""
return _scaled_average(numbers, scalar)
def average_evens(numbers, *, scalar=_default_scalar):
"""Calculate the average of all even numbers in a list.
Args:
numbers (list): list of numbers; may be integers or floats
scalar (float, optional): the scalar that multiplies the
average of the even numbers
Returns:
float: (scaled) average
"""
return _scaled_average([n for n in numbers if n % 2 == 0], scalar)
def average_odds(numbers, *, scalar=_default_scalar):
"""Calculate the average of all odd numbers in a list.
Args:
numbers (list): list of numbers; may be integers or floats
scalar (float, optional): the scalar that multiplies the
average of the even numbers
Returns:
float: (scaled) average
"""
return _scaled_average([n for n in numbers if n % 2 != 0], scalar)

View file

@ -0,0 +1,5 @@
"""This package provides Vectors and Matrices."""
from .matrix import Matrix
from .utils import norm
from .vector import Vector

254
sample_package/matrix.py Normal file
View file

@ -0,0 +1,254 @@
"""This module defines a Matrix class."""
class Matrix:
"""A standard m-by-n-dimensional matrix from linear algebra.
The class is designed for sub-classing in such a way that
the user can adapt the typing class attribute to change,
for example, how the entries are stored (e.g., as integers).
Attributes:
storage (callable): must return an iterable that is used
to store the entries of the matrix; defaults to tuple
typing (callable): type casting applied to all vector
entries upon creation; defaults to float
zero_threshold (float): maximum difference allowed when
comparing an entry to zero; defaults to 1e-12
"""
storage = tuple
typing = float
zero_threshold = 1e-12
def __init__(self, data):
"""Initiate a new matrix.
Args:
data (iterable of iterables): the matrix's entries;
must be provided with rows first, then column;
the number of column entries must be consistent across rows
where the first row sets the standard;
must have at least one element in total
Raises:
ValueError:
- if the number of columns is inconsistent across the rows
- if the provided data do not have enough entries
"""
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("each row must have the same number of entries")
if len(self) == 0:
raise ValueError("the matrix must have at least one entry")
@classmethod
def from_columns(cls, data):
"""Initiate a new matrix.
This is an alternative constructor for data provided in column-major order.
Args:
data (iterable of iterables): the matrix's entries in column-major order;
the number of column entries must be consistent per row
while the first row sets the correct number;
must have at least one element in total
Raises:
ValueError:
- if the number of columns is inconsistent across the rows
- if the provided data do not have enough entries
"""
return cls(data).transpose()
def __repr__(self):
name = self.__class__.__name__
args = ", ".join(
"(" + ", ".join(f"{c:.3f}" for c in r) + ",)" for r in self._entries
)
return f"{name}(({args}))"
def __str__(self):
name = self.__class__.__name__
first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols
return f"{name}(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]"
@property
def n_rows(self):
"""Number of rows in the matrix."""
return len(self._entries)
@property
def n_cols(self):
"""Number of columns in the matrix."""
return len(self._entries[0])
def __len__(self):
return self.n_rows * self.n_cols
def __getitem__(self, index):
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]
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 integer or a tuple of two integers")
def rows(self):
"""Iterate over the rows of the matrix.
Returns:
rows (Generator): produces Vector instances
representing individual rows of the matrix
"""
return (Vector(r) for r in self._entries)
def cols(self):
"""Iterate over the columns of the matrix.
Returns:
columns (Generator): produces Vector instances
representing individual columns of the matrix
"""
return (
Vector(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):
"""Iterate over the entries of the matrix in flat fashion.
Args:
reverse (bool): flag to iterate backwards; defaults to False
row_major (bool): flag to iterate in row major order; defaults to False
Returns:
entries (Generator): produces the entries rows of the matrix
in the type set in the typing class variable
"""
if reverse:
rows, cols = range(self.n_rows - 1, -1, -1), 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):
return self.entries()
def __reversed__(self):
return self.entries(reverse=True)
def __add__(self, other):
if isinstance(other, self.__class__):
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
raise ValueError("matrices need to be of 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)
)
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):
if isinstance(other, Vector):
raise TypeError("vectors and matrices cannot be added")
return self + other
def __sub__(self, other):
return self + (-other)
def __rsub__(self, other):
if isinstance(other, Vector):
raise TypeError("vectors and matrices cannot be subtracted")
return (-self) + other
def _matrix_multiply(self, other):
if self.n_cols != other.n_rows:
raise ValueError("matrices need to have compatible dimensions")
return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows())
def __mul__(self, other):
if isinstance(other, numbers.Number):
return self.__class__((x * other for x in r) for r in self._entries)
elif isinstance(other, Vector):
return self._matrix_multiply(other.as_matrix()).as_vector()
elif isinstance(other, self.__class__):
return self._matrix_multiply(other)
return NotImplemented
def __rmul__(self, other):
if isinstance(other, numbers.Number):
return self * other
elif isinstance(other, Vector):
return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
return NotImplemented
def __truediv__(self, other):
if isinstance(other, numbers.Number):
return self * (1 / other)
return NotImplemented
def __eq__(self, other):
if isinstance(other, self.__class__):
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
raise ValueError("matrices need to be of the same dimensions")
for x, y in zip(self, other):
if abs(x - y) > self.zero_threshold:
return False
return True
return NotImplemented
def __pos__(self):
return self
def __neg__(self):
return self.__class__((-x for x in r) for r in self._entries)
def __abs__(self):
return norm(self)
def __bool__(self):
return bool(abs(self))
def __float__(self):
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):
"""Cast the matrix as a one-dimensional vector.
Returns:
vector (Vector)
Raises:
RuntimeError: if not one of the two dimensions is 1
"""
if not (self.n_rows == 1 or self.n_cols == 1):
raise RuntimeError("one dimension (m or n) must be 1")
return Vector(x for x in self)
def transpose(self):
"""Transpose the rows and columns of the matrix.
Returns:
matrix (Matrix)
"""
return self.__class__(zip(*self._entries))
from .vector import Vector

14
sample_package/utils.py Normal file
View file

@ -0,0 +1,14 @@
"""This module provides utility functions."""
def norm(vector_or_matrix):
"""Calculate the Frobenius or Euclidean norm of a matrix or vector.
Args:
vector_or_matrix (Vector/Matrix): the entries whose squares
are to be summed up
Returns:
norm (float)
"""
return math.sqrt(sum(x ** 2 for x in vector_or_matrix))

141
sample_package/vector.py Normal file
View file

@ -0,0 +1,141 @@
"""This module defines a Vector class."""
from .matrix import Matrix
class Vector:
"""A standard one-dimensional vector from linear algebra.
The class is designed for sub-classing in such a way that
the user can adapt the typing class attribute to change,
for example, how the entries are stored (e.g., as integers).
Attributes:
storage (callable): must return an iterable that is used
to store the entries of the vector; defaults to tuple
typing (callable): type casting applied to all vector
entries upon creation; defaults to float
zero_threshold (float): maximum difference allowed when
comparing an entry to zero; defaults to 1e-12
"""
storage = tuple
typing = float
zero_threshold = 1e-12
def __init__(self, data):
"""Initiate a new vector.
Args:
data (iterable): the vector's entries;
must have at least one element
Raises:
ValueError: if the provided data do not have enough entries
"""
self._entries = self.storage(self.typing(x) for x in data)
if len(self) == 0:
raise ValueError("the vector must have at least one entry")
def __repr__(self):
name, args = self.__class__.__name__, ", ".join(f"{x:.3f}" for x in self)
return f"{name}(({args}))"
def __str__(self):
name, first, last, entries = (
self.__class__.__name__,
self[0],
self[-1],
len(self),
)
return f"{name}({first:.1f}, ..., {last:.1f})[{entries:d}]"
def __len__(self):
return len(self._entries)
def __getitem__(self, index):
if not isinstance(index, int):
raise TypeError("index must be an integer")
return self._entries[index]
def __iter__(self):
return iter(self._entries)
def __reversed__(self):
return reversed(self._entries)
def __add__(self, other):
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors need to be of the same length")
return self.__class__(x + y for (x, y) in zip(self, other))
elif isinstance(other, numbers.Number):
return self.__class__(x + other for x in self)
return NotImplemented
def __radd__(self, other):
return self + other
def __sub__(self, other):
return self + (-other)
def __rsub__(self, other):
return (-self) + other
def __mul__(self, other):
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors need to be of the same length")
return sum(x * y for (x, y) in zip(self, other))
elif isinstance(other, numbers.Number):
return self.__class__(x * other for x in self)
return NotImplemented
def __rmul__(self, other):
return self * other
def __truediv__(self, other):
if isinstance(other, numbers.Number):
return self * (1 / other)
return NotImplemented
def __eq__(self, other):
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors need to be of the same length")
for x, y in zip(self, other):
if abs(x - y) > self.zero_threshold:
return False
return True
return NotImplemented
def __pos__(self):
return self
def __neg__(self):
return self.__class__(-x for x in self)
def __abs__(self):
return norm(self)
def __bool__(self):
return bool(abs(self))
def __float__(self):
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):
"""Convert the vector into a matrix.
Args:
column (bool): if the vector should be interpreted as
as a column vector or not; defaults to True
Returns:
matrix (Matrix)
"""
if column:
return Matrix([x] for x in self)
return Matrix([(x for x in self)])