Release 0.2.0
This commit is contained in:
commit
7ed78c99c7
17 changed files with 6934 additions and 306 deletions
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
"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",
|
||||
|
@ -58,10 +58,10 @@
|
|||
"source": [
|
||||
"To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n",
|
||||
"\n",
|
||||
"- a *solid* understanding of the **English language** (i.e., usage of *technical* terms with *narrow* and *distinct* meanings),\n",
|
||||
"- knowledge of **basic mathematics** from high school (i.e., addition, subtraction, multiplication, division, and a little bit of calculus and statistics),\n",
|
||||
"- the ability to **think conceptually** and **reason logically** (i.e., *not* just memorizing), and\n",
|
||||
"- the willingness to **invest 2-4 hours a day for a month** (cf., \"ABC\"-rule at the end)"
|
||||
"- a *solid* understanding of the **English language**,\n",
|
||||
"- knowledge of **basic mathematics** from high school,\n",
|
||||
"- the ability to **think conceptually** and **reason logically**, and\n",
|
||||
"- the willingness to **invest 2-4 hours a day for a month**."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -83,7 +83,7 @@
|
|||
}
|
||||
},
|
||||
"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": [
|
||||
"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",
|
||||
"- statistics & econometrics\n",
|
||||
"- data cleaning & wrangling\n",
|
||||
|
@ -154,7 +154,7 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
"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",
|
||||
|
@ -180,7 +180,7 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Hello world\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Hello world\")"
|
||||
]
|
||||
|
@ -351,7 +359,8 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
"- by **expressing** the steps of a **computation / process**\n",
|
||||
"- and thereby **documenting** the process in a formal way\n",
|
||||
|
@ -369,7 +378,8 @@
|
|||
}
|
||||
},
|
||||
"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",
|
||||
"- asks **abstract** questions (\"Is something computable at all?\"),\n",
|
||||
"- develops and analyses **algorithms** and **data structures**,\n",
|
||||
|
@ -419,6 +429,8 @@
|
|||
}
|
||||
},
|
||||
"source": [
|
||||
"Here is a brief history of and some background on Python (cf., also this [TechRepublic article](https://www.techrepublic.com/article/python-is-eating-the-world-how-one-developers-side-project-became-the-hottest-programming-language-on-the-planet/) for a more elaborate story):\n",
|
||||
"\n",
|
||||
"- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[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",
|
||||
"- 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",
|
||||
|
@ -446,7 +458,7 @@
|
|||
}
|
||||
},
|
||||
"source": [
|
||||
"Python is a **general-purpose** programming language that allows for **fast development**, is **easy to comprehend**, **open-source**, long established, unifies the knowledge of **hundreds of thousands of experts** around the world, runs on basically every machine, and can handle the complexities of applications involving **big data**."
|
||||
"Python is a **general-purpose** programming language that allows for **fast development**, is **easy to read**, **open-source**, long established, unifies the knowledge of **hundreds of thousands of experts** around the world, runs on basically every machine, and can handle the complexities of applications involving **big data**."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -769,7 +781,7 @@
|
|||
" 1. Elements of a Program\n",
|
||||
" 2. Functions & Modularization\n",
|
||||
"- What is the flow of execution? How can we form sentences from words?\n",
|
||||
" 3. Boolean Logic & Conditionals\n",
|
||||
" 3. Conditionals & Exceptions\n",
|
||||
" 4. Recursion & Looping"
|
||||
]
|
||||
},
|
||||
|
@ -812,7 +824,7 @@
|
|||
}
|
||||
},
|
||||
"source": [
|
||||
"As with every good lecture, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere."
|
||||
"As with every good book, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"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
|
@ -19,7 +19,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"metadata": {},
|
||||
"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",
|
||||
"metadata": {},
|
||||
"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
3478
02_functions.ipynb
Normal file
File diff suppressed because it is too large
Load diff
397
02_functions_review_and_exercises.ipynb
Normal file
397
02_functions_review_and_exercises.ipynb
Normal 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
|
||||
}
|
1805
03_conditionals.ipynb
Normal file
1805
03_conditionals.ipynb
Normal file
File diff suppressed because it is too large
Load diff
389
03_conditionals_review_and_exercises.ipynb
Normal file
389
03_conditionals_review_and_exercises.ipynb
Normal file
|
@ -0,0 +1,389 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"# Chapter 3: Conditionals & Exceptions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Content Review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Read Chapter 3 of the book. Then work through the seven 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 is the **singleton** design pattern? How many objects does the expression `[True, False, True, False]` generate in memory?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q2**: What do we mean when we talk about **truthy** and **falsy** expressions?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q3**: Explain how the conceptual difference between a **statement** and an **expression** relates to the difference between a **conditional statement** and a **conditional expression**."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q4**: Why is the use of **temporary variables** encouraged in Python?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q5**: What does the `finally`-branch enforce in this code snippet? How can a `try` statement be useful *without* an `except`-branch?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"```python\n",
|
||||
"try:\n",
|
||||
" print(\"Make a request to a service on the internet\")\n",
|
||||
"finally:\n",
|
||||
" print(\"This could be clean-up code\")\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"**Q6**: The objects `True`, `False`, and `None` represent the idea of \"yes\", \"no\", and \"maybe\" answers in a natural language.\n",
|
||||
"\n",
|
||||
"Hint: you also respond with a code cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q7**: The `try` statement is useful for handling **syntax** errors."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Coding Exercises"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Discounting Customer Orders"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q8.1**: Write a function `discounted_price()` that takes the positional arguments `unit_price` (of type `float`) and `quantity` (of type `int`) and implements a discount scheme for a line item in a customer order as follows:\n",
|
||||
"\n",
|
||||
"- if the unit price is over 100 dollars, grant 10% relative discount\n",
|
||||
"- if a customer orders more than 10 items, one in every five items is for free\n",
|
||||
"\n",
|
||||
"Only one of the two discounts is granted, whichever is better for the customer.\n",
|
||||
"\n",
|
||||
"The function should then return the overall price for the line item. Do not forget to round appropriately."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q8.2**: Calculate the final price for the following line items of an order:\n",
|
||||
"- $7$ smartphones @ $99.00$ USD\n",
|
||||
"- $3$ workstations @ $999.00$ USD\n",
|
||||
"- $19$ GPUs @ $879.95$ USD\n",
|
||||
"- $14$ Raspberry Pis @ $35.00$ USD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"**Q8.3**: Re-calculate the last two line items with order quantities of $20$ and $15$. What do you observe?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" (your observation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q8.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q8.2** were chosen as they were?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Fizz Buzz revisited"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"When you worked on the Fizz Buzz exercise in Chapter 1, you actually did not know about the `elif` and `else` keywords yet. Well, now you do."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"numbers = list(range(1, 101))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Q9**: Copy and paste your answer to **Q11.2** in Chapter 1 here and instead of three consecutive `if` statements re-write it with *one* compound `if` statement.\n",
|
||||
"\n",
|
||||
"This code will then be a lot more robust as the order of the three `if` statements cannot be screwed up."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
**Important**: The notebooks are being added throughout the fall semester of 2019!
|
||||
|
||||
# An Introduction to Python and Programming
|
||||
|
||||
The purpose of this repository is to serve as an interactive "book" for a
|
||||
|
@ -13,7 +15,9 @@ science professionals and researchers.
|
|||
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)
|
||||
- [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.ipynb)
|
||||
- [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)
|
||||
- [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)
|
||||
|
||||
However, it is recommended that students **install Python and Jupyter
|
||||
locally** and run the code in the notebooks on their own.
|
||||
|
|
11
poetry.lock
generated
11
poetry.lock
generated
|
@ -423,6 +423,14 @@ terminado = ">=0.8.1"
|
|||
tornado = ">=5.0"
|
||||
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]]
|
||||
category = "main"
|
||||
description = "Utilities for writing pandoc filters in python"
|
||||
|
@ -673,7 +681,7 @@ version = "3.5.1"
|
|||
notebook = ">=4.4.1"
|
||||
|
||||
[metadata]
|
||||
content-hash = "431dace6d8d5c3e390b59e964116bb6219fb45c7a8c09cbfe99d70c704a54320"
|
||||
content-hash = "7c3d541c65a27324b49fc7ed2b84067e223eb4e20c20310ff9e485b016f65f91"
|
||||
python-versions = "^3.6"
|
||||
|
||||
[metadata.hashes]
|
||||
|
@ -711,6 +719,7 @@ mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "
|
|||
nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"]
|
||||
nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"]
|
||||
notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"]
|
||||
numpy = ["05dbfe72684cc14b92568de1bc1f41e5f62b00f714afc9adee42f6311738091f", "0d82cb7271a577529d07bbb05cb58675f2deb09772175fab96dc8de025d8ac05", "10132aa1fef99adc85a905d82e8497a580f83739837d7cbd234649f2e9b9dc58", "12322df2e21f033a60c80319c25011194cd2a21294cc66fee0908aeae2c27832", "16f19b3aa775dddc9814e02a46b8e6ae6a54ed8cf143962b4e53f0471dbd7b16", "3d0b0989dd2d066db006158de7220802899a1e5c8cf622abe2d0bd158fd01c2c", "438a3f0e7b681642898fd7993d38e2bf140a2d1eafaf3e89bb626db7f50db355", "5fd214f482ab53f2cea57414c5fb3e58895b17df6e6f5bca5be6a0bb6aea23bb", "73615d3edc84dd7c4aeb212fa3748fb83217e00d201875a47327f55363cef2df", "7bd355ad7496f4ce1d235e9814ec81ee3d28308d591c067ce92e49f745ba2c2f", "7d077f2976b8f3de08a0dcf5d72083f4af5411e8fddacd662aae27baa2601196", "a4092682778dc48093e8bda8d26ee8360153e2047826f95a3f5eae09f0ae3abf", "b458de8624c9f6034af492372eb2fee41a8e605f03f4732f43fc099e227858b2", "e70fc8ff03a961f13363c2c95ef8285e0cf6a720f8271836f852cc0fa64e97c8", "ee8e9d7cad5fe6dde50ede0d2e978d81eafeaa6233fb0b8719f60214cf226578", "f4a4f6aba148858a5a5d546a99280f71f5ee6ec8182a7d195af1a914195b21a2"]
|
||||
pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"]
|
||||
parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"]
|
||||
pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"]
|
||||
|
|
|
@ -8,6 +8,7 @@ license = "MIT"
|
|||
[tool.poetry.dependencies]
|
||||
python = "^3.6"
|
||||
jupyter = "^1.0"
|
||||
numpy = "^1.17"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = {version = "^18.3-alpha.0", allows-prereleases = true}
|
||||
|
|
|
@ -30,6 +30,7 @@ mistune==0.8.4
|
|||
nbconvert==5.6.0
|
||||
nbformat==4.4.0
|
||||
notebook==6.0.1
|
||||
numpy==1.17.2
|
||||
pandocfilters==1.4.2
|
||||
parso==0.5.1
|
||||
pexpect==4.7.0
|
||||
|
|
75
sample_module.py
Normal file
75
sample_module.py
Normal 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)
|
5
sample_package/__init__.py
Normal file
5
sample_package/__init__.py
Normal 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
254
sample_package/matrix.py
Normal 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
14
sample_package/utils.py
Normal 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
141
sample_package/vector.py
Normal 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)])
|
Loading…
Reference in a new issue