diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..fd4c7d6
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,108 @@
+# How to Contribute to CS-Tutorial
+
+## Working on a new feature or fixing a bug
+
+If you would like to add a new feature or fix an existing bug, we prefer that you open a new issue on the CS-Tutorial repository before creating a pull request.
+
+It’s important to note that when opening an issue, you should first do a quick search of existing issues to make sure your suggestion hasn’t already been added as an issue.
+
+**To open a Github issue, go to the CS-Tutorial repository, select “Issues”, “New Issue” then “Feature Request” or “Bug Report” and fill out the template.**
+
+The CS-Tutorial team will then get in touch with you to discuss if the proposed feature aligns with the roadmap, and we will guide you along the way in shaping the proposed feature so that it could be merged to the CS-Tutorial codebase.
+
+## What is a Pull Request (PR)?
+
+This is how the GitHub team defines a PR:
+
+> “Pull requests let you tell others about changes you’ve pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch.”
+
+This process is used by both CS-Tutorial team members and CS-Tutorial contributors to make changes and improvements to CS-Tutorial.
+
+## What to know before opening a PR
+
+### Draft PRs
+
+If you're ready to get some quick initial feedback from the CS-Tutorial team, you can create a draft pull request.
+
+### PRs should be a reasonable length
+
+If your PR is greater than 500 lines, please consider splitting it into multiple smaller contributions.
+
+
+## How to open a PR and contribute code to CS-Tutorial Open Source
+
+### 1. Forking the CS-Tutorial Repository
+
+Head to CS-Tutorial repository and click ‘Fork’. Forking a repository creates you a copy of the project which you can edit and use to propose changes to the original project.
+
+Once you fork it, a copy of the CS-Tutorial repository will appear inside your GitHub repository list.
+
+### 2. Cloning the Forked Repository Locally
+
+To make changes to your copy of the CS-Tutorial repository, clone the repository on your local machine. To do that, run the following command in your terminal:
+
+```
+git clone https://github.com/hejazizo/CS-Tutorial.git
+```
+
+The link to the repository can be found after clicking Clone or download button as shown in the image below:
+
+Note: this assumes you have git installed on your local machine. If not, check out the [following guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to learn how to install it.
+
+### 3. Update your Forked Repository
+
+Before you make any changes to your cloned repository, make sure you have the latest version of the original https://github.com/hejazizo/CS-Tutorial.git repository. To do that, run the following commands in your terminal:
+
+```
+cd CS-Tutorial
+git remote add upstream https://github.com/hejazizo/CS-Tutorial.git
+git pull upstream main
+```
+
+This will update the local copy of the CS-Tutorial repository to the latest version.
+
+### 4. Implement your code contribution
+
+At this point, you are good to make changes to the files in the local directory of your project.
+
+Alternatively, you can create a new branch which will contain the implementation of your contribution. To do that, run:
+
+```
+git checkout -b name-of-your-new-branch
+```
+
+### 5. Push changes to your forked repository on GitHub
+
+Once you are happy with the changes you made in the local files, push them to the forked repository on GitHub. To do that, run the following commands:
+
+```
+git add .
+git commit -m ‘fixed a bug’
+git push origin name-of-your-new-branch
+```
+
+This will create a new branch on your forked CS-Tutorial repository, and now you’re ready to create a Pull Request with your proposed changes!
+
+### 6. Opening the Pull Request on CS-Tutorial
+
+Head to the forked repository and click on a _Compare & pull_ request button.
+
+This will open a window where you can choose the repository and branch you would like to propose your changes to, as well as specific details of your contribution. In the top panel menu choose the following details:
+
+- Base repository: `hejazizo/cs-tutorial`
+- Base branch: `main`
+- Head repository: `your-github-username/cs-tutorial`
+- Head branch: `name-of-your-new-branch`
+
+Once you are happy with everything, click the _Create pull request_ button. This will create a Pull Request with your proposed changes.
+
+### 8. Merging your PR and the final steps of your contribution
+
+Once you open a PR, a member from the CS-Tutorial team will get in touch with you with the feedback on your contribution. In some cases, contributions are accepted right away, but often, you may be asked to make some edits/improvements. Don’t worry if you are asked to change something - it’s a completely normal part of software development.
+
+If you have been requested to make changes to your contribution, head back to the local copy of your repository on your machine, implement the changes and push them to your contribution branch by repeating instructions from step 5. Your pull request will automatically be updated with the changes you pushed. Once you've implemented all of the suggested changes, tag the person who first reviewed your contribution by mentioning them in the comments of your PR to ask them to take another look.
+Finally, if your contribution is accepted, the CS-Tutorial team member will merge it to the CS-Tutorial codebase.
+
+### 10. Non-code contributions
+
+Contributing doesn’t start and end with code. You can support the project by planning community events, creating tutorials, helping fellow community members find answers to their questions or translating documentation and news. Every contribution matters!
diff --git a/python/01. Basics/00 Introduction.ipynb b/Python/01. Basics/01 Introduction.ipynb
similarity index 75%
rename from python/01. Basics/00 Introduction.ipynb
rename to Python/01. Basics/01 Introduction.ipynb
index dd798b6..3279a57 100755
--- a/python/01. Basics/00 Introduction.ipynb
+++ b/Python/01. Basics/01 Introduction.ipynb
@@ -4,7 +4,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Introduction\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Introduction \n",
"\n",
"- Python was created in 1990 by [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum).\n",
"- Python 3 released in 2008.\n",
@@ -12,6 +19,27 @@
"- High focus on readability of code."
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Why Choose Python](#why_choose_python)\n",
+ " * [Python is Popular](#python_is_popular)\n",
+ " * [Python is Interpreted](#python_is_interpreted)\n",
+ " * [Python is Free](#python_is_free)\n",
+ " * [Python is Portable](#python_is_portable)\n",
+ " * [Python is Simple](#python_is_simple)\n",
+ " * [But It’s Not That Simple](#but_it’s_not_that_simple)\n",
+ "* [What can you do with python?](#what_can_you_do_with_python?)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -44,7 +72,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Why Choose Python\n",
+ "\n",
+ "## Why Choose Python [
](#table_of_contents)\n",
"\n",
"If you’re going to write programs, there are literally dozens of commonly used languages to choose from. Why choose Python? Here are some of the features that make Python an appealing choice."
]
@@ -53,7 +82,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Python is Popular\n",
+ "# \n",
+ "### Python is Popular [
](#table_of_contents)\n",
+ "\n",
"Python has been growing in popularity over the last few years. The [2018 Stack Overflow Developer Survey](https://insights.stackoverflow.com/survey/2018) ranked Python as the 7th most popular and the number one most wanted technology of the year. [World-class software development companies around the globe use Python every single day](https://realpython.com/world-class-companies-using-python/).\n",
"\n",
"According to [research by Dice](https://insights.dice.com/2016/02/01/whats-hot-and-not-in-tech-skills/) Python is also one of the hottest skills to have and the most popular programming language in the world based on the [Popularity of Programming Language Index](https://pypl.github.io/PYPL.html).\n",
@@ -65,7 +96,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Python is Interpreted\n",
+ "\n",
+ "### Python is Interpreted [
](#table_of_contents)\n",
+ "\n",
"Many languages are compiled, meaning the source code you create needs to be translated into machine code, the language of your computer’s processor, before it can be run. Programs written in an interpreted language are passed straight to an interpreter that runs them directly.\n",
"\n",
"This makes for a quicker development cycle because you just type in your code and run it, without the intermediate compilation step.\n",
@@ -86,7 +119,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Python is Free\n",
+ "\n",
+ "### Python is Free [
](#table_of_contents)\n",
+ "\n",
"The Python interpreter is developed under an OSI-approved open-source license, making it free to install, use, and distribute, even for commercial purposes.\n",
"\n",
"A version of the interpreter is available for virtually any platform there is, including all flavors of Unix, Windows, macOS, smartphones and tablets, and probably anything else you ever heard of. A version even exists for the half dozen people remaining who use OS/2."
@@ -96,7 +131,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Python is Portable\n",
+ "\n",
+ "### Python is Portable [
](#table_of_contents)\n",
+ "\n",
"Because Python code is interpreted and not compiled into native machine instructions, code written for one platform will work on any other platform that has the Python interpreter installed. (This is true of any interpreted language, not just Python.)"
]
},
@@ -104,7 +141,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Python is Simple\n",
+ "\n",
+ "### Python is Simple [
](#table_of_contents)\n",
+ "\n",
"As programming languages go, Python is relatively uncluttered, and the developers have deliberately kept it that way.\n",
"\n",
"A rough estimate of the complexity of a language can be gleaned from the number of keywords or reserved words in the language. These are words that are reserved for special meaning by the compiler or interpreter because they designate specific built-in functionality of the language.\n",
@@ -118,7 +157,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### But It’s Not That Simple\n",
+ "\n",
+ "### But It’s Not That Simple [
](#table_of_contents)\n",
+ "\n",
"For all its syntactical simplicity, Python supports most constructs that would be expected in a very high-level language, including complex dynamic data types, structured and functional programming, and [object-oriented programming](https://realpython.com/python3-object-oriented-programming/).\n",
"\n",
"Additionally, a very extensive library of classes and functions is available that provides capability well beyond what is built into the language, such as database manipulation or GUI programming.\n",
@@ -130,7 +171,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## What can you do with python?\n",
+ "\n",
+ "## What can you do with python? [
](#table_of_contents)\n",
+ "\n",
"- Automate Simple tasks.\n",
" * Searching for file and editing them.\n",
" * Scraping information from a website.\n",
@@ -152,13 +195,22 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
"This section gave an overview of the Python programming language, including:\n",
"\n",
"A brief history of the development of Python\n",
"Some reasons why you might select Python as your language of choice\n",
"Python is a great option, whether you are a beginning programmer looking to learn the basics, an experienced programmer designing a large application, or anywhere in between. The basics of Python are easily grasped, and yet its capabilities are vast."
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
@@ -177,7 +229,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/02 Conda Environments.ipynb b/Python/01. Basics/02 Conda Environments.ipynb
similarity index 90%
rename from python/01. Basics/02 Conda Environments.ipynb
rename to Python/01. Basics/02 Conda Environments.ipynb
index 1ff6137..31388fb 100755
--- a/python/01. Basics/02 Conda Environments.ipynb
+++ b/Python/01. Basics/02 Conda Environments.ipynb
@@ -2,38 +2,46 @@
"cells": [
{
"cell_type": "markdown",
- "id": "d4a95e9d",
"metadata": {},
"source": [
- "# Conda Environments"
+ "
"
]
},
{
"cell_type": "markdown",
- "id": "scheduled-witness",
"metadata": {},
"source": [
+ "#
Conda Environments \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
"## Table of Contents\n",
- "* [1. Understanding Conda Environments](#u-conda-environments)\n",
- "* [2. Understanding Basic Package Management With Conda](#basic-package-management)\n",
- " * [2.1 Searching and Installing Packages](#search-install-packager)\n",
- " * [2.2 Updating and Removing Packages](#u-r-packages)\n",
- "* [3. Cheat Sheet](#cheat-sheet)\n",
- "* [4. Read More](#read-more)"
+ "\n",
+ "\n",
+ "* [Understanding Conda Environments](#understanding_conda_environments)\n",
+ "* [Understanding Basic Package Management With Conda](#understanding_basic_package_management_with_conda)\n",
+ " * [Searching and Installing Packages](#searching_and_installing_packages)\n",
+ " * [Updating and Removing Packages](#updating_and_removing_packages)\n",
+ "* [Cheat Sheet](#cheat_sheet)\n",
+ "* [
Read More](#read_more)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "successful-abortion",
"metadata": {},
"source": [
- " \n",
- "## Understanding Conda Environments"
+ "\n",
+ "## Understanding Conda Environments [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "reported-recovery",
"metadata": {},
"source": [
"When you start developing a project from scratch, it’s recommended that you use the latest versions of the libraries you need. However, when working with someone else’s project, such as when running an example from [Kaggle](https://www.kaggle.com/) or [Github](https://github.com/), you may need to install specific versions of packages or even another version of Python due to compatibility issues."
@@ -41,7 +49,6 @@
},
{
"cell_type": "markdown",
- "id": "critical-tenant",
"metadata": {},
"source": [
"This problem may also occur when you try to run an application you’ve developed long ago, which uses a particular library version that does not work with your application anymore due to updates."
@@ -49,7 +56,6 @@
},
{
"cell_type": "markdown",
- "id": "lovely-korea",
"metadata": {},
"source": [
"Virtual environments are a solution to this kind of problem. By using them, it is possible to create multiple environments, each one with different versions of packages. A typical Python set up includes [Virtualenv](https://virtualenv.pypa.io/en/stable/#), a tool to create isolated Python virtual environments, widely used in the Python community."
@@ -57,7 +63,6 @@
},
{
"cell_type": "markdown",
- "id": "frozen-pavilion",
"metadata": {},
"source": [
"Conda includes its own environment manager and presents some advantages over Virtualenv, especially concerning numerical applications, such as the ability to manage non-Python dependencies and the ability to manage different versions of Python, which is not possible with Virtualenv. Besides that, Conda environments are entirely compatible with default [Python packages](https://realpython.com/python-modules-packages/) that may be installed using pip."
@@ -65,7 +70,6 @@
},
{
"cell_type": "markdown",
- "id": "latter-bankruptcy",
"metadata": {},
"source": [
"Miniconda installation provides Conda and a root environment with a version of Python and some basic packages installed. Besides this root environment, it is possible to set up additional environments including different versions of Python and packages."
@@ -73,9 +77,9 @@
},
{
"cell_type": "markdown",
- "id": "inner-allowance",
"metadata": {},
"source": [
+ "\n",
"Using the Anaconda prompt, it is possible to check the available Conda environments by running `conda env list`:\n",
"\n",
"```bash\n",
@@ -90,9 +94,9 @@
},
{
"cell_type": "markdown",
- "id": "lasting-prevention",
"metadata": {},
"source": [
+ "\n",
"This base environment is the root environment, created by the Miniconda installer. It is possible to create another environment, named `otherenv`, by running `conda create --name otherenv`:\n",
"\n",
"\n",
@@ -123,7 +127,6 @@
},
{
"cell_type": "markdown",
- "id": "limited-persian",
"metadata": {},
"source": [
"As notified after the environment creation process is finished, it is possible to activate the otherenv environment by running `conda activate otherenv`. You’ll notice the environment has changed by the indication between parentheses in the beginning of the prompt:\n",
@@ -136,7 +139,6 @@
},
{
"cell_type": "markdown",
- "id": "roman-multimedia",
"metadata": {},
"source": [
"You can open the Python interpreter within this environment by running `python`:\n",
@@ -152,7 +154,6 @@
},
{
"cell_type": "markdown",
- "id": "hired-knife",
"metadata": {},
"source": [
"The environment includes Python 3.7.0, the same version included in the root base environment. To exit the Python interpreter, just run `quit()`:\n",
@@ -166,7 +167,6 @@
},
{
"cell_type": "markdown",
- "id": "fatal-lunch",
"metadata": {},
"source": [
"To deactivate the otherenv environment and go back to the root base environment, you should run `deactivate`:\n",
@@ -180,9 +180,9 @@
},
{
"cell_type": "markdown",
- "id": "textile-integrity",
"metadata": {},
"source": [
+ "\n",
"As mentioned earlier, Conda allows you to easily create environments with different versions of Python, which is not straightforward with Virtualenv. To include a different Python version within an environment, you have to specify it by using `python=` when running conda create. For example, to create an environment named `py2` with `Python 2.7`, you have to run `conda create --name py2 python=2.7`:\n",
"\n",
"\n",
@@ -229,7 +229,6 @@
},
{
"cell_type": "markdown",
- "id": "labeled-november",
"metadata": {},
"source": [
"As shown by the output of `conda create`, this time some new packages were installed, since the new environment uses Python 2. You can check the new environment indeed uses Python 2 by activating it and running the Python interpreter:\n",
@@ -241,9 +240,9 @@
},
{
"cell_type": "markdown",
- "id": "obvious-cycle",
"metadata": {},
"source": [
+ "\n",
"Now, if you run `conda env list`, you should see the two environments that were created, besides the root base environment:\n",
"\n",
"```bash\n",
@@ -261,9 +260,9 @@
},
{
"cell_type": "markdown",
- "id": "known-timothy",
"metadata": {},
"source": [
+ "\n",
"In the list, the asterisk indicates the activated environment. It is possible to remove an environment by running `conda remove --name --all`. Since it is not possible to remove an activated environment, you should first deactivate the `py2` environment, to remove it:\n",
"\n",
"```bash\n",
@@ -298,7 +297,6 @@
},
{
"cell_type": "markdown",
- "id": "widespread-mixture",
"metadata": {},
"source": [
"Now that you’ve covered the basics of managing environments with Conda, let’s see how to manage packages within the environments."
@@ -306,20 +304,19 @@
},
{
"cell_type": "markdown",
- "id": "diagnostic-fisher",
"metadata": {},
"source": [
- " \n",
- "## Understanding Basic Package Management With Conda\n",
+ "\n",
+ "## Understanding Basic Package Management With Conda [
](#table_of_contents)\n",
"\n",
"Within each environment, packages of software can be installed using the Conda package manager. The root base environment created by the Miniconda installer includes some packages by default that are not part of Python standard library."
]
},
{
"cell_type": "markdown",
- "id": "enabling-bolivia",
"metadata": {},
"source": [
+ "\n",
"The default installation includes the minimum packages necessary to use Conda. To check the list of installed packages in an environment, you just have to make sure it is activated and run `conda list`. In the root environment, the following packages are installed by default:\n",
"\n",
"```bash\n",
@@ -362,7 +359,6 @@
},
{
"cell_type": "markdown",
- "id": "indoor-savannah",
"metadata": {},
"source": [
"To manage the packages, you should also use Conda. Next, let’s see how to search, install, update, and remove packages using Conda."
@@ -370,11 +366,10 @@
},
{
"cell_type": "markdown",
- "id": "coral-governor",
"metadata": {},
"source": [
- " \n",
- "### Searching and Installing Packages\n",
+ "\n",
+ "### Searching and Installing Packages [
](#table_of_contents)\n",
"\n",
"Packages are installed from repositories called **channels** by Conda, and some default channels are configured by the installer. To search for a specific package, you can run `conda search `. For example, this is how you search for the `keras` package (a machine learning library):\n",
"\n",
@@ -395,7 +390,6 @@
},
{
"cell_type": "markdown",
- "id": "cardiovascular-zimbabwe",
"metadata": {},
"source": [
"According to the previous output, there are different versions of the package and different builds for each version, such as for Python 3.5 and 3.6."
@@ -403,9 +397,9 @@
},
{
"cell_type": "markdown",
- "id": "mighty-impact",
"metadata": {},
"source": [
+ "\n",
"The previous search shows only exact matches for packages named `keras`. To perform a broader search, including all packages containing `keras` in their names, you should use the wildcard `*`. For example, when you run conda search `*keras*`, you get the following:\n",
"\n",
"```bash\n",
@@ -436,7 +430,6 @@
},
{
"cell_type": "markdown",
- "id": "simplified-freedom",
"metadata": {},
"source": [
"As the previous output shows, there are some other keras related packages in the default channels."
@@ -444,9 +437,9 @@
},
{
"cell_type": "markdown",
- "id": "promising-digest",
"metadata": {},
"source": [
+ "\n",
"To install a package, you should run `conda install `. By default, the newest version of the package will be installed in the active environment. So, let’s install the package `keras` in the environment `otherenv` that you’ve already created:\n",
"\n",
"```bash\n",
@@ -514,7 +507,6 @@
},
{
"cell_type": "markdown",
- "id": "elementary-milan",
"metadata": {},
"source": [
"Conda manages the necessary dependencies for a package when it is installed. Since the package keras has a lot of dependencies, when you install it, Conda manages to install this big list of packages."
@@ -522,7 +514,6 @@
},
{
"cell_type": "markdown",
- "id": "appreciated-australia",
"metadata": {},
"source": [
"> **Note:** The paragraph below may not happen when you run it as newer versions of `keras` may be available that use python 3.7.\n",
@@ -532,7 +523,6 @@
},
{
"cell_type": "markdown",
- "id": "seven-filename",
"metadata": {},
"source": [
"Sometimes, you don’t want packages to be downgraded, and it would be better to just create a new environment with the necessary version of Python. To check the list of new packages, updates, and downgrades necessary for a package without installing it, you should use the parameter `--dry-run`. For example, to check the packages that will be changed by the installation of the package keras, you should run the following:\n",
@@ -544,9 +534,9 @@
},
{
"cell_type": "markdown",
- "id": "ordinary-member",
"metadata": {},
"source": [
+ "\n",
"However, if necessary, it is possible to change the default Python of a Conda environment by installing a specific version of the package python. To demonstrate that, let’s create a new environment called envpython:\n",
"\n",
"```bash\n",
@@ -576,7 +566,6 @@
},
{
"cell_type": "markdown",
- "id": "blind-samba",
"metadata": {},
"source": [
"As you saw before, since the root base environment uses Python 3.7, envpython is created including this same version of Python:\n",
@@ -592,9 +581,9 @@
},
{
"cell_type": "markdown",
- "id": "dependent-norwegian",
"metadata": {},
"source": [
+ "\n",
"To install a specific version of a package, you can run `conda install =`. For example, this is how you install Python 3.6 in the envpython environment:\n",
"\n",
"```bash\n",
@@ -626,9 +615,9 @@
},
{
"cell_type": "markdown",
- "id": "infinite-western",
"metadata": {},
"source": [
+ "\n",
"In case you need to install more than one package in an environment, it is possible to run conda install only once, passing the names of the packages. To illustrate that, let’s install `numpy`, `scipy`, and `matplotlib`, basic packages for numerical computation:\n",
"\n",
"```bash\n",
@@ -708,7 +697,6 @@
},
{
"cell_type": "markdown",
- "id": "impossible-intent",
"metadata": {},
"source": [
"Now that you’ve covered how to search and install packages, let’s see how to update and remove them using Conda."
@@ -716,20 +704,19 @@
},
{
"cell_type": "markdown",
- "id": "acting-brief",
"metadata": {},
"source": [
- " \n",
- "### Updating and Removing Packages\n",
+ "\n",
+ "### Updating and Removing Packages [
](#table_of_contents)\n",
"\n",
"Sometimes, when new packages are released, you need to update them. To do so, you may run `conda update `. In case you wish to update all the packages within one environment, you should activate the environment and run `conda update --all`."
]
},
{
"cell_type": "markdown",
- "id": "listed-seeking",
"metadata": {},
"source": [
+ "\n",
"To remove a package, you can run `conda remove `. For example, this is how you remove numpy from the root base environment:\n",
"\n",
"```bash\n",
@@ -758,7 +745,6 @@
},
{
"cell_type": "markdown",
- "id": "metropolitan-helping",
"metadata": {},
"source": [
"> **Note:** It’s worth noting that when you remove a package, all packages that depend on it are also removed."
@@ -766,27 +752,24 @@
},
{
"cell_type": "markdown",
- "id": "satisfactory-fourth",
"metadata": {},
"source": [
- " \n",
- "## Cheat Sheet\n",
+ "\n",
+ "## Cheat Sheet [
](#table_of_contents)\n",
"\n",
"[Click here to get access to a Conda cheat sheet](https://static.realpython.com/conda-cheatsheet.pdf) with handy usage examples for managing your Python environment and packages."
]
},
{
"cell_type": "markdown",
- "id": "confident-dynamics",
"metadata": {},
"source": [
- " \n",
- "## Read More"
+ "\n",
+ "##
Read More [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "silent-hawaii",
"metadata": {},
"source": [
"Also, if you’d like a deeper understanding of Anaconda and Conda, check out the following links:\n",
@@ -812,7 +795,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/03 Interacting with Python.ipynb b/Python/01. Basics/03 Interacting with Python.ipynb
similarity index 82%
rename from python/01. Basics/03 Interacting with Python.ipynb
rename to Python/01. Basics/03 Interacting with Python.ipynb
index e8cb5e2..478941d 100755
--- a/python/01. Basics/03 Interacting with Python.ipynb
+++ b/Python/01. Basics/03 Interacting with Python.ipynb
@@ -4,25 +4,40 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Interacting with Python"
+ "
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
+ "#
Interacting with Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
"## Table of Contents\n",
- "* [Hello World!](#hello-world)\n",
- "* [Different Ways to Execute Python Code](#execute-python)\n",
- " * [1. Using the Python Interpreter Interactively](#python-interpreter)\n",
- " * [2. IPython](#ipython)\n",
- " * [3. Notebook](#notebook)\n",
- " * [4. Online Python REPL Sites](#online-repl)\n",
- " * [5. Running a Python Script from the Command Line](#command-line)\n",
- " * [6. Interacting with Python through an IDE](#ide)\n",
- " * [7. Text Editors](#text-editors)\n",
- "* [Conclusion](#conclusion)\n",
- "* [FAQ](#faq)"
+ "\n",
+ "\n",
+ "* [Hello, World!](#hello,_world!)\n",
+ "* [Different Ways to Execute Python Code](#different_ways_to_execute_python_code)\n",
+ " * [1. Using the Python Interpreter Interactively](#1._using_the_python_interpreter_interactively)\n",
+ " * [Starting the Interpreter](#starting_the_interpreter)\n",
+ " * [Executing Python Code](#executing_python_code)\n",
+ " * [Exiting the Interpreter](#exiting_the_interpreter)\n",
+ " * [2. IPython](#2._ipython)\n",
+ " * [3. Notebook](#3._notebook)\n",
+ " * [4. Online Python REPL Sites](#4._online_python_repl_sites)\n",
+ " * [5. Running a Python Script from the Command Line](#5._running_a_python_script_from_the_command_line)\n",
+ " * [6. Interacting with Python through an IDE](#6._interacting_with_python_through_an_ide)\n",
+ " * [7. Text Editors](#7._text_editors)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "* [FAQ](#faq)\n",
+ "\n",
+ "---"
]
},
{
@@ -36,7 +51,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "- There are several ways to run Python code.\n",
+ "There are several ways to run Python code.\n",
"- First let's discuss the various options for development environments. There are 3 main types of environments:\n",
" * Text Editors\n",
" * Full IDEs\n",
@@ -50,8 +65,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "## Hello, World!\n",
+ "\n",
+ "## Hello, World! [
](#table_of_contents)\n",
"\n",
"There is a long-standing custom in the field of computer programming that the first code written in a newly installed language is a short program that simply displays the string Hello, World! to the console."
]
@@ -80,16 +95,16 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "## Different Ways to Execute Python Code"
+ "\n",
+ "## Different Ways to Execute Python Code [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 1. Using the Python Interpreter Interactively\n",
+ "\n",
+ "### 1. Using the Python Interpreter Interactively [
](#table_of_contents)\n",
"\n",
"The most straightforward way to start talking to Python is in an interactive [Read-Eval-Print Loop (REPL)](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) environment. That simply means starting up the interpreter and typing commands to it directly. The interpreter:\n",
"\n",
@@ -105,7 +120,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Starting the Interpreter\n",
+ "\n",
+ "#### Starting the Interpreter [
](#table_of_contents)\n",
"\n",
"you can open a terminal window and run the interpreter from the command line. How you go about opening a terminal window varies depending on which operating system you’re using:\n",
"\n",
@@ -147,7 +163,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Executing Python Code\n",
+ "\n",
+ "#### Executing Python Code [
](#table_of_contents)\n",
"\n",
"If you are seeing the prompt, you’re off and running! The next step is to execute the statement that displays `Hello, World!` to the console:\n",
"\n",
@@ -186,7 +203,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Exiting the Interpreter\n",
+ "\n",
+ "#### Exiting the Interpreter [
](#table_of_contents)\n",
+ "\n",
"When you are finished interacting with the interpreter, you can exit a REPL session in several ways:\n",
"\n",
"- Type `exit()` and press Enter:\n",
@@ -212,8 +231,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 2. IPython\n",
+ "\n",
+ "### 2. IPython [
](#table_of_contents)\n",
"\n",
"[IPython](https://ipython.org/install.html) is an upgraded Python [read-eval-print loop (REPL)](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) that makes editing code in a live interpreter session more straightforward and prettier. Here’s what an IPython REPL session looks like:\n",
"\n",
@@ -258,8 +277,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 3. Notebook\n",
+ "\n",
+ "### 3. Notebook [
](#table_of_contents)\n",
"\n",
"A slightly more featureful alternative to a REPL is a **notebook**. Notebooks are a slightly different style of writing Python than standard scripts, though. Instead of a traditional Python file, they give you a series of mini-scripts called **cells** that you can run and re-run in whatever order you want, all in the same Python memory session."
]
@@ -327,8 +346,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 4. Online Python REPL Sites\n",
+ "\n",
+ "### 4. Online Python REPL Sites [
](#table_of_contents)\n",
"\n",
"There are [websites available](https://realpython.com/installing-python/#online-python-interpreters) that can provide you with interactive access to a Python interpreter online without you having to install anything locally."
]
@@ -371,8 +390,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 5. Running a Python Script from the Command Line"
+ "\n",
+ "### 5. Running a Python Script from the Command Line [
](#table_of_contents)"
]
},
{
@@ -462,8 +481,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 6. Interacting with Python through an IDE\n",
+ "\n",
+ "### 6. Interacting with Python through an IDE [
](#table_of_contents)\n",
"\n",
"An Integrated Development Environment (IDE) is an application that more or less combines all the functionality you have seen so far. IDEs usually provide REPL capability as well as an editor with which you can create and modify code to then submit to the interpreter for execution."
]
@@ -484,8 +503,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### 7. Text Editors\n",
+ "\n",
+ "### 7. Text Editors [
](#table_of_contents)\n",
"\n",
"- General editors for any text files.\n",
"- Work with a variety of file types.\n",
@@ -523,7 +542,8 @@
"metadata": {},
"source": [
"\n",
- "## Conclusion\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
"Larger applications are typically contained in script files that are passed to the Python interpreter for execution."
]
},
@@ -553,7 +573,7 @@
"metadata": {},
"source": [
"\n",
- "## FAQ"
+ "## FAQ [
](#table_of_contents)"
]
},
{
@@ -602,7 +622,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/04 Basic Data Types.ipynb b/Python/01. Basics/04 Basic Data Types.ipynb
similarity index 85%
rename from python/01. Basics/04 Basic Data Types.ipynb
rename to Python/01. Basics/04 Basic Data Types.ipynb
index 448b2ea..cf65f8a 100755
--- a/python/01. Basics/04 Basic Data Types.ipynb
+++ b/Python/01. Basics/04 Basic Data Types.ipynb
@@ -4,29 +4,61 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Basic Data Types"
+ "
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Now you know how to interact with the Python interpreter and execute Python code. It’s time to dig into the Python language. First up is a discussion of the basic data types that are built into Python."
+ "#
Basic Data Types \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "* [Integers](#integers)\n",
+ "* [Floating-Point Numbers](#floating-point_numbers)\n",
+ "* [Complex Numbers](#complex_numbers)\n",
+ " * [FAQ](#faq)\n",
+ "* [Strings](#strings)\n",
+ " * [Escape Sequences in Strings](#escape_sequences_in_strings)\n",
+ " * [Suppressing Special Character Meaning](#suppressing_special_character_meaning)\n",
+ " * [Applying Special Meaning to Characters](#applying_special_meaning_to_characters)\n",
+ " * [Raw Strings](#raw_strings)\n",
+ " * [Triple-Quoted Strings](#triple-quoted_strings)\n",
+ "* [Boolean Type, Boolean Context, and “Truthiness”](#boolean_type,_boolean_context,_and_“truthiness”)\n",
+ "* [Built-In Functions](#built-in_functions)\n",
+ " * [Math](#math)\n",
+ " * [Type Conversion](#type_conversion)\n",
+ " * [Iterables and Iterators](#iterables_and_iterators)\n",
+ " * [Composite Data Type](#composite_data_type)\n",
+ " * [Classes, Attributes, and Inheritance](#classes,_attributes,_and_inheritance)\n",
+ " * [Input/Output](#input/output)\n",
+ " * [Variables, References, and Scope](#variables,_references,_and_scope)\n",
+ " * [Miscellaneous](#miscellaneous)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "## Basic Data Types"
+ "Now you know how to interact with the Python interpreter and execute Python code. It’s time to dig into the Python language. First up is a discussion of the basic data types that are built into Python."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Integers"
+ "\n",
+ "## Integers [
](#table_of_contents)\n"
]
},
{
@@ -270,7 +302,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Floating-Point Numbers\n",
+ "\n",
+ "## Floating-Point Numbers [
](#table_of_contents)\n",
"\n",
"The float type in Python designates a floating-point number. `float` values are specified with a decimal point. Optionally, the character `e` or `E` followed by a positive or negative integer may be appended to specify [scientific notation](https://en.wikipedia.org/wiki/Scientific_notation):"
]
@@ -453,23 +486,24 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Complex Numbers\n",
+ "\n",
+ "## Complex Numbers [
](#table_of_contents)\n",
"\n",
"Complex numbers are specified as `+j`. For example:"
]
},
{
"cell_type": "code",
- "execution_count": 88,
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "(2+3j)"
+ "complex"
]
},
- "execution_count": 88,
+ "execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@@ -502,7 +536,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### FAQ\n",
+ "\n",
+ "#### FAQ [
](#table_of_contents)\n",
"\n",
"1. What's the difference between floating point and an integer?\n",
"\n",
@@ -538,7 +573,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Strings\n",
+ "\n",
+ "## Strings [
](#table_of_contents)\n",
"\n",
"Strings are sequences of character data. The string type in Python is called `str`."
]
@@ -728,7 +764,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Escape Sequences in Strings\n",
+ "\n",
+ "### Escape Sequences in Strings [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"Sometimes, you want Python to interpret a character or sequence of characters within a string differently. This may occur in one of two ways:\n",
"- You may want to suppress the special interpretation that certain characters are usually given within a string.\n",
"- You may want to apply special interpretation to characters in a string which would normally be taken literally."
@@ -752,7 +791,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Suppressing Special Character Meaning\n"
+ "\n",
+ "#### Suppressing Special Character Meaning [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -960,7 +1001,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Applying Special Meaning to Characters\n",
+ "\n",
+ "### Applying Special Meaning to Characters [
](#table_of_contents)\n",
+ "\n",
"\n",
"Next, suppose you need to create a string that contains a tab character in it. Some text editors may allow you to insert a tab character directly into your code. But many programmers consider that poor practice, for several reasons:\n",
"\n",
@@ -1131,7 +1174,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Raw Strings\n",
+ "\n",
+ "### Raw Strings [
](#table_of_contents)\n",
+ "\n",
"\n",
"A raw string literal is preceded by r or R, which specifies that escape sequences in the associated string are not translated. The backslash character is left in the string:"
]
@@ -1209,7 +1254,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Triple-Quoted Strings\n",
+ "\n",
+ "### Triple-Quoted Strings [
](#table_of_contents)\n",
+ "\n",
"\n",
"There is yet another way of delimiting strings in Python. Triple-quoted strings are delimited by matching groups of three single quotes or three double quotes. Escape sequences still work in triple-quoted strings, but single quotes, double quotes, and newlines can be included without escaping them. This provides a convenient way to create a string with both single and double quotes in it:"
]
@@ -1265,7 +1312,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Boolean Type, Boolean Context, and “Truthiness”"
+ "\n",
+ "## Boolean Type, Boolean Context, and “Truthiness” [
](#table_of_contents)"
]
},
{
@@ -1340,7 +1388,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Built-In Functions\n",
+ "\n",
+ "## Built-In Functions [
](#table_of_contents)\n",
+ "\n",
"\n",
"The Python interpreter supports many functions that are built-in: sixty-eight, as of Python 3.6. You will cover many of these in the following discussions, as they come up in context."
]
@@ -1356,7 +1406,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Math\n",
+ "\n",
+ "### Math [
](#table_of_contents)\n",
+ "\n",
"\n",
"|Function | Description |\n",
"|:--|:--|\n",
@@ -1373,7 +1425,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Type Conversion\n",
+ "\n",
+ "### Type Conversion [
](#table_of_contents)\n",
+ "\n",
"\n",
"| Function | Description |\n",
"|:--|:--|\n",
@@ -1396,7 +1450,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Iterables and Iterators\n",
+ "\n",
+ "### Iterables and Iterators [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"|Function | Description|\n",
"|:--|:--|\n",
"|`all()`| Returns True if all elements of an iterable are true|\n",
@@ -1418,7 +1475,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Composite Data Type\n",
+ "\n",
+ "### Composite Data Type [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"|Function | Description|\n",
"|:--|:--|\n",
"| `bytearray()` | \tCreates and returns an object of the bytearray class |\n",
@@ -1435,7 +1495,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Classes, Attributes, and Inheritance\n",
+ "\n",
+ "### Classes, Attributes, and Inheritance [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"|Function | Description|\n",
"|:--|:--|\n",
"| `classmethod()` |\tReturns a class method for a function |\n",
@@ -1453,7 +1516,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Input/Output\n",
+ "\n",
+ "### Input/Output [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"|Function | Description|\n",
"|:--|:--|\n",
"|`format()` |\tConverts a value to a formatted representation |\n",
@@ -1466,7 +1532,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Variables, References, and Scope\n",
+ "\n",
+ "### Variables, References, and Scope [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"|Function | Description|\n",
"|:--|:--|\n",
"|`dir()` | Returns a list of names in current local scope or a list of object attributes |\n",
@@ -1480,7 +1549,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Miscellaneous\n",
+ "\n",
+ "### Miscellaneous [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"|Function | Description|\n",
"|:--|:--|\n",
"|`callable()` | Returns True if object appears callable |\n",
@@ -1498,13 +1570,23 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"In this section, you learned about the built-in data types and functions Python provides.\n",
"\n",
"The examples given so far have all manipulated and displayed only constant values. In most programs, you are usually going to want to create objects that change in value as the program executes.\n",
"\n",
"Head to the next section to learn about Python variables."
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
@@ -1523,7 +1605,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/05 Variables.ipynb b/Python/01. Basics/05 Variables.ipynb
similarity index 88%
rename from python/01. Basics/05 Variables.ipynb
rename to Python/01. Basics/05 Variables.ipynb
index 6a6dbc1..3d6bd3b 100755
--- a/python/01. Basics/05 Variables.ipynb
+++ b/Python/01. Basics/05 Variables.ipynb
@@ -2,18 +2,42 @@
"cells": [
{
"cell_type": "markdown",
- "id": "a55f4b8f",
"metadata": {},
"source": [
- "# Variables in Python\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Variables in Python \n",
"\n",
"In the previous tutorial on Basic Data Types in Python, you saw how values of various Python data types can be created. But so far, all the values shown have been literal or constant values:"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Variable Assignment](#variable_assignment)\n",
+ "* [Variable Types in Python](#variable_types_in_python)\n",
+ "* [Object References](#object_references)\n",
+ "* [Object Identity](#object_identity)\n",
+ "* [Variable Names](#variable_names)\n",
+ "* [Reserved Words (Keywords)](#reserved_words_(keywords))\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 1,
- "id": "73ada819",
"metadata": {},
"outputs": [
{
@@ -30,7 +54,6 @@
},
{
"cell_type": "markdown",
- "id": "a369bcd2",
"metadata": {},
"source": [
"If you’re writing more complex code, your program will need data that can change as program execution proceeds."
@@ -38,10 +61,11 @@
},
{
"cell_type": "markdown",
- "id": "90e3c3d7",
"metadata": {},
"source": [
- "## Variable Assignment\n",
+ "\n",
+ "## Variable Assignment [
](#table_of_contents)\n",
+ "\n",
"\n",
"Think of a variable as a name attached to a particular object. In Python, variables need not be declared or defined in advance, as is the case in many other programming languages. To create a variable, you just assign it a value and then start using it. Assignment is done with a single equals sign (`=`):"
]
@@ -49,7 +73,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "b6f94fff",
"metadata": {},
"outputs": [],
"source": [
@@ -58,7 +81,6 @@
},
{
"cell_type": "markdown",
- "id": "e4f4f1fa",
"metadata": {},
"source": [
"This is read or interpreted as “n is assigned the value 300.” Once this is done, n can be used in a statement or expression, and its value will be substituted:"
@@ -67,7 +89,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "659a5389",
"metadata": {},
"outputs": [
{
@@ -84,7 +105,6 @@
},
{
"cell_type": "markdown",
- "id": "5f17dad1",
"metadata": {},
"source": [
"Just as a literal value can be displayed directly from the interpreter prompt in a REPL session without the need for `print()`, so can a variable:"
@@ -93,7 +113,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "2a6a7f6d",
"metadata": {},
"outputs": [
{
@@ -113,7 +132,6 @@
},
{
"cell_type": "markdown",
- "id": "9d6c6a97",
"metadata": {},
"source": [
"Later, if you change the value of `n` and use it again, the new value will be substituted instead:"
@@ -122,7 +140,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "6bd9d37e",
"metadata": {},
"outputs": [],
"source": [
@@ -132,7 +149,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "1aa1a866",
"metadata": {},
"outputs": [
{
@@ -150,7 +166,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "4556ebb9",
"metadata": {},
"outputs": [
{
@@ -170,7 +185,6 @@
},
{
"cell_type": "markdown",
- "id": "62a2e72c",
"metadata": {},
"source": [
"Python also allows chained assignment, which makes it possible to assign the same value to several variables simultaneously:"
@@ -179,7 +193,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "9bc7a297",
"metadata": {},
"outputs": [],
"source": [
@@ -189,7 +202,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "410b6037",
"metadata": {},
"outputs": [
{
@@ -206,7 +218,6 @@
},
{
"cell_type": "markdown",
- "id": "e502bf9e",
"metadata": {},
"source": [
"The chained assignment above assigns 300 to the variables `a`, `b`, and `c` simultaneously."
@@ -214,17 +225,17 @@
},
{
"cell_type": "markdown",
- "id": "1b58f80f",
"metadata": {},
"source": [
- "## Variable Types in Python\n",
+ "\n",
+ "## Variable Types in Python [
](#table_of_contents)\n",
+ "\n",
"\n",
"In many programming languages, variables are statically typed. That means a variable is initially declared to have a specific data type, and any value assigned to it during its lifetime must always have that type."
]
},
{
"cell_type": "markdown",
- "id": "ef366d10",
"metadata": {},
"source": [
"Variables in Python are not subject to this restriction. In Python, a variable may be assigned a value of one type and then later re-assigned a value of a different type:"
@@ -233,7 +244,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "62406cb7",
"metadata": {},
"outputs": [],
"source": [
@@ -243,7 +253,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "98a95bde",
"metadata": {},
"outputs": [
{
@@ -264,7 +273,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "f60605cf",
"metadata": {},
"outputs": [
{
@@ -282,7 +290,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "0fa521ba",
"metadata": {},
"outputs": [],
"source": [
@@ -292,7 +299,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "f3e7bb1e",
"metadata": {},
"outputs": [
{
@@ -309,17 +315,17 @@
},
{
"cell_type": "markdown",
- "id": "62f69cdc",
"metadata": {},
"source": [
- "## Object References\n",
+ "\n",
+ "## Object References [
](#table_of_contents)\n",
+ "\n",
"\n",
"What is actually happening when you make a variable assignment? This is an important question in Python, because the answer differs somewhat from what you’d find in many other programming languages."
]
},
{
"cell_type": "markdown",
- "id": "685bda63",
"metadata": {},
"source": [
"Python is a highly [object-oriented language](https://realpython.com/python3-object-oriented-programming/). In fact, virtually every item of data in a Python program is an object of a specific type or class. (This point will be reiterated many times over the course of these tutorials.)"
@@ -327,7 +333,6 @@
},
{
"cell_type": "markdown",
- "id": "59dca951",
"metadata": {},
"source": [
"Consider this code:"
@@ -336,7 +341,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "c4762544",
"metadata": {},
"outputs": [
{
@@ -353,7 +357,6 @@
},
{
"cell_type": "markdown",
- "id": "728f46fa",
"metadata": {},
"source": [
"When presented with the statement `print(300)`, the interpreter does the following:\n",
@@ -365,7 +368,6 @@
},
{
"cell_type": "markdown",
- "id": "16e1ea59",
"metadata": {},
"source": [
"You can see that an integer object is created using the built-in `type()` function:"
@@ -374,7 +376,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "c707a7dd",
"metadata": {},
"outputs": [
{
@@ -394,7 +395,6 @@
},
{
"cell_type": "markdown",
- "id": "f13f3e7f",
"metadata": {},
"source": [
"A Python variable is a symbolic name that is a reference or [pointer](https://realpython.com/pointers-in-python/) to an object. Once an object is assigned to a variable, you can refer to the object by that name. But the data itself is still contained within the object."
@@ -402,7 +402,6 @@
},
{
"cell_type": "markdown",
- "id": "ada11109",
"metadata": {},
"source": [
"For example:"
@@ -411,7 +410,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "0a722e9e",
"metadata": {},
"outputs": [],
"source": [
@@ -420,7 +418,6 @@
},
{
"cell_type": "markdown",
- "id": "b8ca1dee",
"metadata": {},
"source": [
"This assignment creates an integer object with the value `300` and assigns the variable `n` to point to that object."
@@ -428,7 +425,6 @@
},
{
"cell_type": "markdown",
- "id": "95705a19",
"metadata": {},
"source": [
"
"
@@ -436,7 +432,6 @@
},
{
"cell_type": "markdown",
- "id": "22951859",
"metadata": {},
"source": [
"The following code verifies that `n` points to an integer object:"
@@ -445,7 +440,6 @@
{
"cell_type": "code",
"execution_count": 23,
- "id": "46ec7241",
"metadata": {},
"outputs": [
{
@@ -465,7 +459,6 @@
},
{
"cell_type": "markdown",
- "id": "c7cda3d9",
"metadata": {},
"source": [
"Now consider the following statement:"
@@ -474,7 +467,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "1a189f08",
"metadata": {},
"outputs": [],
"source": [
@@ -483,7 +475,6 @@
},
{
"cell_type": "markdown",
- "id": "027e3b78",
"metadata": {},
"source": [
"What happens when it is executed? Python does not create another object. It simply creates a new symbolic name or reference, `m`, which points to the same object that `n` points to."
@@ -491,7 +482,6 @@
},
{
"cell_type": "markdown",
- "id": "31b5337d",
"metadata": {},
"source": [
"
"
@@ -499,7 +489,6 @@
},
{
"cell_type": "markdown",
- "id": "9a115aed",
"metadata": {},
"source": [
"Next, suppose you do this:"
@@ -508,7 +497,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "a71ed9aa",
"metadata": {},
"outputs": [],
"source": [
@@ -517,7 +505,6 @@
},
{
"cell_type": "markdown",
- "id": "3e709f8a",
"metadata": {},
"source": [
"Now Python creates a new integer object with the value 400, and m becomes a reference to it."
@@ -525,7 +512,6 @@
},
{
"cell_type": "markdown",
- "id": "ae298100",
"metadata": {},
"source": [
"
"
@@ -533,7 +519,6 @@
},
{
"cell_type": "markdown",
- "id": "4c76d609",
"metadata": {},
"source": [
"Lastly, suppose this statement is executed next:"
@@ -542,7 +527,6 @@
{
"cell_type": "code",
"execution_count": 75,
- "id": "6d83f4dc",
"metadata": {},
"outputs": [],
"source": [
@@ -551,7 +535,6 @@
},
{
"cell_type": "markdown",
- "id": "84361b93",
"metadata": {},
"source": [
"Now Python creates a string object with the value `\"foo\"` and makes `n` reference that."
@@ -559,7 +542,6 @@
},
{
"cell_type": "markdown",
- "id": "a2fae845",
"metadata": {},
"source": [
"
"
@@ -567,7 +549,6 @@
},
{
"cell_type": "markdown",
- "id": "3b740cee",
"metadata": {},
"source": [
"There is no longer any reference to the integer object 300. It is orphaned, and there is no way to access it."
@@ -575,7 +556,6 @@
},
{
"cell_type": "markdown",
- "id": "f0fc675b",
"metadata": {},
"source": [
"Tutorials in this series will occasionally refer to the lifetime of an object. An object’s life begins when it is created, at which time at least one reference to it is created. During an object’s lifetime, additional references to it may be created, as you saw above, and references to it may be deleted as well. An object stays alive, as it were, so long as there is at least one reference to it."
@@ -583,7 +563,6 @@
},
{
"cell_type": "markdown",
- "id": "86c07d02",
"metadata": {},
"source": [
"When the number of references to an object drops to zero, it is no longer accessible. At that point, its lifetime is over. Python will eventually notice that it is inaccessible and reclaim the allocated memory so it can be used for something else. In computer lingo, this process is referred to as [garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29)."
@@ -591,18 +570,18 @@
},
{
"cell_type": "markdown",
- "id": "d9cc212c",
"metadata": {},
"source": [
- "## Object Identity\n",
+ "\n",
+ "## Object Identity [
](#table_of_contents)\n",
+ "\n",
"\n",
"In Python, every object that is created is given a number that uniquely identifies it. It is guaranteed that no two objects will have the same identifier during any period in which their lifetimes overlap. Once an object’s reference count drops to zero and it is garbage collected, as happened to the `300` object above, then its identifying number becomes available and may be used again."
]
},
{
"cell_type": "code",
- "execution_count": 41,
- "id": "e02aa413",
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -611,7 +590,6 @@
},
{
"cell_type": "markdown",
- "id": "efe835b7",
"metadata": {},
"source": [
"The built-in Python function `id()` returns an object’s integer identifier. Using the `id()` function, you can verify that two variables indeed point to the same object:"
@@ -619,8 +597,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
- "id": "aee3dbe1",
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -630,17 +607,16 @@
},
{
"cell_type": "code",
- "execution_count": 27,
- "id": "c1d721b9",
+ "execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "139989024260592"
+ "140238739082320"
]
},
- "execution_count": 27,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@@ -651,17 +627,65 @@
},
{
"cell_type": "code",
- "execution_count": 28,
- "id": "f5457fa1",
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "139989024260592"
+ "140238739082320"
]
},
- "execution_count": 28,
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "id(m)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "True"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "m is n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m = 300"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "140238472208624"
+ ]
+ },
+ "execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@@ -673,7 +697,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "dad48b6d",
"metadata": {},
"outputs": [],
"source": [
@@ -683,7 +706,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "467b9feb",
"metadata": {},
"outputs": [
{
@@ -703,7 +725,6 @@
},
{
"cell_type": "markdown",
- "id": "182c51cd",
"metadata": {},
"source": [
"After the assignment `m = n`, m and n both point to the same object, confirmed by the fact that `id(m)` and `id(n)` return the same number. Once `m` is reassigned to `400`, `m` and `n` point to different objects with different identities."
@@ -711,7 +732,6 @@
},
{
"cell_type": "markdown",
- "id": "4beb1faa",
"metadata": {},
"source": [
"> **Deep Dive: Caching Small Integer Values**\n",
@@ -746,17 +766,17 @@
},
{
"cell_type": "markdown",
- "id": "554ec3b2",
"metadata": {},
"source": [
- "## Variable Names\n",
+ "\n",
+ "## Variable Names [
](#table_of_contents)\n",
+ "\n",
"\n",
"The examples you have seen so far have used short, terse variable names like `m` and `n`. But variable names can be more verbose. In fact, it is usually beneficial if they are because it makes the purpose of the variable more evident at first glance."
]
},
{
"cell_type": "markdown",
- "id": "e82b25ba",
"metadata": {},
"source": [
"Officially, variable names in Python can be any length and can consist of uppercase and lowercase letters (`A-Z, a-z`), digits (`0-9`), and the underscore character (`_`). An additional restriction is that, although a variable name can contain digits, the first character of a variable name cannot be a digit."
@@ -764,7 +784,6 @@
},
{
"cell_type": "markdown",
- "id": "0613da94",
"metadata": {},
"source": [
"For example, all of the following are valid variable names:"
@@ -773,7 +792,6 @@
{
"cell_type": "code",
"execution_count": 124,
- "id": "71ff48d3",
"metadata": {},
"outputs": [],
"source": [
@@ -785,7 +803,6 @@
{
"cell_type": "code",
"execution_count": 125,
- "id": "9262196a",
"metadata": {},
"outputs": [
{
@@ -802,7 +819,6 @@
},
{
"cell_type": "markdown",
- "id": "b6186f85",
"metadata": {},
"source": [
"But this one is not, because a variable name can’t begin with a digit:"
@@ -811,7 +827,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "b8f5a16d",
"metadata": {},
"outputs": [
{
@@ -829,7 +844,6 @@
},
{
"cell_type": "markdown",
- "id": "357c83fe",
"metadata": {},
"source": [
"Note that case is significant. Lowercase and uppercase letters are not the same. Use of the underscore character is significant as well. Each of the following defines a different variable:"
@@ -838,7 +852,6 @@
{
"cell_type": "code",
"execution_count": 132,
- "id": "30bd1c5e",
"metadata": {},
"outputs": [],
"source": [
@@ -855,7 +868,6 @@
{
"cell_type": "code",
"execution_count": 133,
- "id": "73d8b415",
"metadata": {},
"outputs": [
{
@@ -872,7 +884,6 @@
},
{
"cell_type": "markdown",
- "id": "5705d917",
"metadata": {},
"source": [
"There is nothing stopping you from creating two different variables in the same program called `age` and `Age`, or for that matter `agE`. But it is probably ill-advised. It would certainly be likely to confuse anyone trying to read your code, and even you yourself, after you’d been away from it awhile."
@@ -880,7 +891,6 @@
},
{
"cell_type": "markdown",
- "id": "75073119",
"metadata": {},
"source": [
"It is worthwhile to give a variable a name that is descriptive enough to make clear what it is being used for. For example, suppose you are tallying the number of people who have graduated college. You could conceivably choose any of the following:"
@@ -889,7 +899,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "0410fffa",
"metadata": {},
"outputs": [],
"source": [
@@ -903,7 +912,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "e5ccd23e",
"metadata": {},
"outputs": [
{
@@ -922,7 +930,6 @@
},
{
"cell_type": "markdown",
- "id": "c15edc69",
"metadata": {},
"source": [
"All of them are probably better choices than n, or ncg, or the like. At least you can tell from the name what the value of the variable is supposed to represent."
@@ -930,7 +937,6 @@
},
{
"cell_type": "markdown",
- "id": "c088f09d",
"metadata": {},
"source": [
"On the other hand, they aren’t all necessarily equally legible. As with many things, it is a matter of personal preference, but most people would find the first two examples, where the letters are all shoved together, to be harder to read, particularly the one in all capital letters. The most commonly used methods of constructing a multi-word variable name are the last three examples:\n",
@@ -946,7 +952,6 @@
},
{
"cell_type": "markdown",
- "id": "7c3741c2",
"metadata": {},
"source": [
"Programmers debate hotly, with surprising fervor, which of these is preferable. Decent arguments can be made for all of them. Use whichever of the three is most visually appealing to you. Pick one and use it consistently."
@@ -954,7 +959,6 @@
},
{
"cell_type": "markdown",
- "id": "b23e6fa7",
"metadata": {},
"source": [
"You will see later that variables aren’t the only things that can be given names. You can also name functions, classes, modules, and so on. The rules that apply to variable names also apply to identifiers, the more general term for names given to program objects."
@@ -962,7 +966,6 @@
},
{
"cell_type": "markdown",
- "id": "5ef84fed",
"metadata": {},
"source": [
"The [Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/), also known as [PEP 8](https://www.python.org/dev/peps/pep-0008/#naming-conventions), contains Naming Conventions that list suggested standards for names of different object types. PEP 8 includes the following recommendations:\n",
@@ -973,16 +976,17 @@
},
{
"cell_type": "markdown",
- "id": "2e62149e",
"metadata": {},
"source": [
- "## Reserved Words (Keywords)\n",
+ "\n",
+ "## Reserved Words (Keywords) [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"There is one more restriction on identifier names. The Python language reserves a small set of keywords that designate special language functionality. No object can have the same name as a reserved word."
]
},
{
"cell_type": "markdown",
- "id": "7fc64770",
"metadata": {},
"source": [
"In Python 3.6, there are 33 reserved keywords:"
@@ -990,7 +994,6 @@
},
{
"cell_type": "markdown",
- "id": "e27da392",
"metadata": {},
"source": [
"| Python Keywords| | | |\n",
@@ -1008,7 +1011,6 @@
},
{
"cell_type": "markdown",
- "id": "4c47d2ef",
"metadata": {},
"source": [
"You can see this list any time by typing `help(\"keywords\")` to the Python interpreter. Reserved words are case-sensitive and must be used exactly as shown. They are all entirely lowercase, except for `False`, `None`, and `True`."
@@ -1017,7 +1019,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "f2512ced",
"metadata": {},
"outputs": [
{
@@ -1046,7 +1047,6 @@
},
{
"cell_type": "markdown",
- "id": "aee81202",
"metadata": {},
"source": [
"Trying to create a variable with the same name as any reserved word results in an error:"
@@ -1055,7 +1055,6 @@
{
"cell_type": "code",
"execution_count": 103,
- "id": "bef74b45",
"metadata": {},
"outputs": [
{
@@ -1073,17 +1072,17 @@
},
{
"cell_type": "markdown",
- "id": "273d69e2",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
"\n",
"This section covered the basics of Python **variables**, including object references and identity, and naming of Python identifiers."
]
},
{
"cell_type": "markdown",
- "id": "e5278d8f",
"metadata": {},
"source": [
"You now have a good understanding of some of Python’s data types and know how to create variables that reference objects of those types."
@@ -1091,7 +1090,6 @@
},
{
"cell_type": "markdown",
- "id": "05f507d5",
"metadata": {},
"source": [
"Next, you will see how to combine data objects into **expressions** involving various **operations**."
@@ -1114,7 +1112,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/06 Operators and Expressions.ipynb b/Python/01. Basics/06 Operators and Expressions.ipynb
similarity index 86%
rename from python/01. Basics/06 Operators and Expressions.ipynb
rename to Python/01. Basics/06 Operators and Expressions.ipynb
index e89c745..70afee7 100755
--- a/python/01. Basics/06 Operators and Expressions.ipynb
+++ b/Python/01. Basics/06 Operators and Expressions.ipynb
@@ -2,17 +2,48 @@
"cells": [
{
"cell_type": "markdown",
- "id": "f8651a61",
"metadata": {},
"source": [
- "# Operators and Expressions\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Operators and Expressions \n",
"\n",
"In Python, operators are special symbols that designate that some sort of computation should be performed. The values that an operator acts on are called **operands**."
]
},
{
"cell_type": "markdown",
- "id": "b78854e4",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Arithmetic Operators](#arithmetic_operators)\n",
+ "* [Comparison Operators](#comparison_operators)\n",
+ " * [Equality Comparison on Floating-Point Values](#equality_comparison_on_floating-point_values)\n",
+ "* [Logical Operators](#logical_operators)\n",
+ " * [Logical Expressions Involving Boolean Operands](#logical_expressions_involving_boolean_operands)\n",
+ " * [Evaluation of Non-Boolean Values in Boolean Context](#evaluation_of_non-boolean_values_in_boolean_context)\n",
+ " * [Numeric Value](#numeric_value)\n",
+ " * [String](#string)\n",
+ " * [Built-In Composite Data Object](#built-in_composite_data_object)\n",
+ " * [The `None` Keyword](#the_`none`_keyword)\n",
+ "* [Bitwise Operators](#bitwise_operators)\n",
+ "* [Identity Operators](#identity_operators)\n",
+ "* [Augmented Assignment Operators](#augmented_assignment_operators)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
"metadata": {},
"source": [
"Here is an example:"
@@ -21,7 +52,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "5512030a",
"metadata": {},
"outputs": [],
"source": [
@@ -32,7 +62,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "f44b233c",
"metadata": {},
"outputs": [
{
@@ -52,7 +81,6 @@
},
{
"cell_type": "markdown",
- "id": "03565296",
"metadata": {},
"source": [
"In this case, the `+` operator adds the operands `a` and `b` together. An operand can be either a literal value or a variable that references an object:"
@@ -61,7 +89,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "e24a4145",
"metadata": {},
"outputs": [],
"source": [
@@ -72,7 +99,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "9078d73c",
"metadata": {},
"outputs": [
{
@@ -92,7 +118,6 @@
},
{
"cell_type": "markdown",
- "id": "22b29bb5",
"metadata": {},
"source": [
"A sequence of operands and operators, like `a + b - 5`, is called an **expression**. Python supports many operators for combining data objects into expressions. These are explored below."
@@ -100,15 +125,15 @@
},
{
"cell_type": "markdown",
- "id": "303a9ee7",
"metadata": {},
"source": [
- "## Arithmetic Operators"
+ "\n",
+ "## Arithmetic Operators [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "04837e1a",
"metadata": {},
"source": [
"The following table lists the arithmetic operators supported by Python:\n",
@@ -129,7 +154,6 @@
},
{
"cell_type": "markdown",
- "id": "375a058e",
"metadata": {},
"source": [
"Here are some examples of these operators in use:"
@@ -138,7 +162,6 @@
{
"cell_type": "code",
"execution_count": 104,
- "id": "a4de71c8",
"metadata": {},
"outputs": [],
"source": [
@@ -149,7 +172,6 @@
{
"cell_type": "code",
"execution_count": 105,
- "id": "5f5d03cd",
"metadata": {},
"outputs": [
{
@@ -170,7 +192,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "65c205ce",
"metadata": {},
"outputs": [
{
@@ -191,7 +212,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "9b0cbbd3",
"metadata": {},
"outputs": [
{
@@ -212,7 +232,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "246ae037",
"metadata": {},
"outputs": [
{
@@ -233,7 +252,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "00f73585",
"metadata": {},
"outputs": [
{
@@ -254,7 +272,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "b6e7f296",
"metadata": {},
"outputs": [
{
@@ -275,7 +292,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "7382b6dd",
"metadata": {},
"outputs": [
{
@@ -296,7 +312,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "388d3e91",
"metadata": {},
"outputs": [
{
@@ -316,7 +331,6 @@
},
{
"cell_type": "markdown",
- "id": "83dbcf0b",
"metadata": {},
"source": [
"The result of standard division (`/`) is always a `float`, even if the dividend is evenly divisible by the divisor:"
@@ -325,7 +339,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "624752ff",
"metadata": {},
"outputs": [
{
@@ -346,7 +359,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "e004f456",
"metadata": {},
"outputs": [
{
@@ -366,7 +378,6 @@
},
{
"cell_type": "markdown",
- "id": "a173be58",
"metadata": {},
"source": [
"When the result of floor division (`//`) is positive, it is as though the fractional portion is truncated off, leaving only the integer portion. When the result is negative, the result is rounded down to the next smallest (greater negative) integer:"
@@ -375,7 +386,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "b700d679",
"metadata": {},
"outputs": [
{
@@ -396,7 +406,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "0b6ca1e8",
"metadata": {},
"outputs": [
{
@@ -417,7 +426,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "c0f3863a",
"metadata": {},
"outputs": [
{
@@ -438,7 +446,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "9a6f1503",
"metadata": {},
"outputs": [
{
@@ -459,7 +466,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "d48fb428",
"metadata": {},
"outputs": [
{
@@ -479,10 +485,12 @@
},
{
"cell_type": "markdown",
- "id": "2fef3c6c",
"metadata": {},
"source": [
- "## Comparison Operators\n",
+ "\n",
+ "## Comparison Operators [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"\n",
"|Operator |\tExample |\tMeaning |\tResult |\n",
"|:--|:--|:--|:--|\n",
@@ -496,7 +504,6 @@
},
{
"cell_type": "markdown",
- "id": "fc3710b3",
"metadata": {},
"source": [
"Here are examples of the comparison operators in use:"
@@ -505,7 +512,6 @@
{
"cell_type": "code",
"execution_count": 23,
- "id": "b3f2f9f3",
"metadata": {},
"outputs": [],
"source": [
@@ -516,7 +522,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "56dc7d50",
"metadata": {},
"outputs": [
{
@@ -537,7 +542,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "76a974d8",
"metadata": {},
"outputs": [
{
@@ -558,7 +562,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "61605dab",
"metadata": {},
"outputs": [
{
@@ -579,7 +582,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "891c5f7d",
"metadata": {},
"outputs": [
{
@@ -600,7 +602,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "1cb729fa",
"metadata": {},
"outputs": [],
"source": [
@@ -611,7 +612,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "ffd289cd",
"metadata": {},
"outputs": [
{
@@ -632,7 +632,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "695a4c00",
"metadata": {},
"outputs": [
{
@@ -653,7 +652,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "2047b30a",
"metadata": {},
"outputs": [
{
@@ -673,7 +671,6 @@
},
{
"cell_type": "markdown",
- "id": "aa8f7c1f",
"metadata": {},
"source": [
"Comparison operators are typically used in Boolean contexts like conditional and loop statements to direct program flow, as you will see later."
@@ -681,18 +678,19 @@
},
{
"cell_type": "markdown",
- "id": "737dd052",
"metadata": {},
"source": [
- "### Equality Comparison on Floating-Point Values\n",
+ "\n",
+ "### Equality Comparison on Floating-Point Values [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"\n",
"Recall from the earlier discussion of floating-point numbers that the value stored internally for a float object may not be precisely what you’d think it would be. For that reason, it is poor practice to compare floating-point values for exact equality. Consider this example:"
]
},
{
"cell_type": "code",
- "execution_count": 45,
- "id": "f9a93596",
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
@@ -701,7 +699,7 @@
"False"
]
},
- "execution_count": 45,
+ "execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
@@ -713,7 +711,6 @@
},
{
"cell_type": "markdown",
- "id": "b228ea16",
"metadata": {},
"source": [
"Yikes! The internal representations of the addition operands are not exactly equal to `1.1` and `2.2`, so you cannot rely on x to compare exactly to `3.3`."
@@ -721,7 +718,6 @@
},
{
"cell_type": "markdown",
- "id": "47dc1cee",
"metadata": {},
"source": [
"The preferred way to determine whether two floating-point values are “equal” is to compute whether they are close to one another, given some tolerance. Take a look at this example:"
@@ -730,7 +726,6 @@
{
"cell_type": "code",
"execution_count": 127,
- "id": "7390ff8f",
"metadata": {},
"outputs": [],
"source": [
@@ -740,7 +735,6 @@
{
"cell_type": "code",
"execution_count": 128,
- "id": "0fd7b119",
"metadata": {},
"outputs": [
{
@@ -760,7 +754,6 @@
},
{
"cell_type": "markdown",
- "id": "fb041d2e",
"metadata": {},
"source": [
"`abs()` returns absolute value. If the absolute value of the difference between the two numbers is less than the specified tolerance, they are close enough to one another to be considered equal."
@@ -768,20 +761,22 @@
},
{
"cell_type": "markdown",
- "id": "497e4d2a",
"metadata": {},
"source": [
- "## Logical Operators\n",
+ "\n",
+ "## Logical Operators [
](#table_of_contents)\n",
+ "\n",
"\n",
"The logical operators `not`, `or`, `and` and modify and join together expressions evaluated in Boolean context to create more complex conditions."
]
},
{
"cell_type": "markdown",
- "id": "cba77d31",
"metadata": {},
"source": [
- "### Logical Expressions Involving Boolean Operands\n",
+ "\n",
+ "### Logical Expressions Involving Boolean Operands [
](#table_of_contents)\n",
+ "\n",
"\n",
"As you have seen, some objects and expressions in Python actually are of Boolean type. That is, they are equal to one of the Python objects `True` or `False`."
]
@@ -789,7 +784,6 @@
{
"cell_type": "code",
"execution_count": 35,
- "id": "aabf38a1",
"metadata": {},
"outputs": [
{
@@ -811,7 +805,6 @@
{
"cell_type": "code",
"execution_count": 41,
- "id": "4d2afeb9",
"metadata": {},
"outputs": [
{
@@ -831,7 +824,6 @@
},
{
"cell_type": "markdown",
- "id": "86fab03a",
"metadata": {},
"source": [
"|Operator |\tExample\t |Meaning |\n",
@@ -843,7 +835,6 @@
},
{
"cell_type": "markdown",
- "id": "4febd04c",
"metadata": {},
"source": [
"Take a look at how they work in practice below."
@@ -852,7 +843,6 @@
{
"cell_type": "code",
"execution_count": 137,
- "id": "b41c32a6",
"metadata": {},
"outputs": [],
"source": [
@@ -862,7 +852,6 @@
{
"cell_type": "code",
"execution_count": 138,
- "id": "a1c405d3",
"metadata": {},
"outputs": [
{
@@ -883,7 +872,6 @@
{
"cell_type": "code",
"execution_count": 139,
- "id": "704e8a42",
"metadata": {},
"outputs": [],
"source": [
@@ -893,7 +881,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "9f3f352a",
"metadata": {},
"outputs": [
{
@@ -914,7 +901,6 @@
{
"cell_type": "code",
"execution_count": 58,
- "id": "689e5edb",
"metadata": {},
"outputs": [
{
@@ -934,17 +920,17 @@
},
{
"cell_type": "markdown",
- "id": "c2e4557d",
"metadata": {},
"source": [
- "### Evaluation of Non-Boolean Values in Boolean Context\n",
+ "\n",
+ "### Evaluation of Non-Boolean Values in Boolean Context [
](#table_of_contents)\n",
+ "\n",
"\n",
"Many objects and expressions are not equal to True or False. Nonetheless, they may still be evaluated in Boolean context and determined to be “truthy” or “falsy.”"
]
},
{
"cell_type": "markdown",
- "id": "6681ab29",
"metadata": {},
"source": [
"So what is true and what isn’t? As a philosophical question, that is outside the scope of this tutorial!"
@@ -952,7 +938,6 @@
},
{
"cell_type": "markdown",
- "id": "7c1bddeb",
"metadata": {},
"source": [
"But in Python, it is well-defined. All the following are considered false when evaluated in Boolean context:\n",
@@ -965,7 +950,6 @@
},
{
"cell_type": "markdown",
- "id": "ef2285d4",
"metadata": {},
"source": [
"Virtually any other object built into Python is regarded as `True`."
@@ -973,7 +957,6 @@
},
{
"cell_type": "markdown",
- "id": "c3933c47",
"metadata": {},
"source": [
"You can determine the “truthiness” of an object or expression with the built-in `bool()` function. `bool()` returns `True` if its argument is truthy and `False` if it is falsy."
@@ -981,10 +964,12 @@
},
{
"cell_type": "markdown",
- "id": "3950c626",
"metadata": {},
"source": [
- "#### Numeric Value\n",
+ "\n",
+ "#### Numeric Value [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"- A zero value is false.\n",
"- A non-zero value is true."
]
@@ -992,7 +977,6 @@
{
"cell_type": "code",
"execution_count": 154,
- "id": "e9050915",
"metadata": {},
"outputs": [
{
@@ -1010,7 +994,6 @@
{
"cell_type": "code",
"execution_count": 155,
- "id": "96c8a566",
"metadata": {},
"outputs": [
{
@@ -1027,10 +1010,12 @@
},
{
"cell_type": "markdown",
- "id": "0c37818b",
"metadata": {},
"source": [
- "#### String\n",
+ "\n",
+ "#### String [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"- An empty string is false.\n",
"- A non-empty string is true."
]
@@ -1038,7 +1023,6 @@
{
"cell_type": "code",
"execution_count": 156,
- "id": "8f27bdff",
"metadata": {},
"outputs": [
{
@@ -1056,7 +1040,6 @@
{
"cell_type": "code",
"execution_count": 157,
- "id": "c348a752",
"metadata": {},
"outputs": [
{
@@ -1073,10 +1056,12 @@
},
{
"cell_type": "markdown",
- "id": "0a36f504",
"metadata": {},
"source": [
- "#### Built-In Composite Data Object\n",
+ "\n",
+ "#### Built-In Composite Data Object [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"> Python provides built-in composite data types called `list`, `tuple`, `dict`, and `set` (and some more). These are “container” types that contain other objects. An object of one of these types is considered false if it is empty and true if it is non-empty.\n",
">\n",
">The examples below demonstrate this for the list type. (Lists are defined in Python with square brackets.)\n",
@@ -1087,7 +1072,6 @@
{
"cell_type": "code",
"execution_count": 158,
- "id": "d99a9d30",
"metadata": {},
"outputs": [
{
@@ -1108,7 +1092,6 @@
{
"cell_type": "code",
"execution_count": 159,
- "id": "70bafc98",
"metadata": {},
"outputs": [
{
@@ -1129,7 +1112,6 @@
{
"cell_type": "code",
"execution_count": 160,
- "id": "1ba859a9",
"metadata": {},
"outputs": [
{
@@ -1150,7 +1132,6 @@
{
"cell_type": "code",
"execution_count": 161,
- "id": "c27e0e0c",
"metadata": {},
"outputs": [
{
@@ -1170,17 +1151,18 @@
},
{
"cell_type": "markdown",
- "id": "b3317357",
"metadata": {},
"source": [
- "#### The `None` Keyword\n",
+ "\n",
+ "#### The `None` Keyword [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"- `None` is always false:"
]
},
{
"cell_type": "code",
"execution_count": 162,
- "id": "0eb097aa",
"metadata": {},
"outputs": [
{
@@ -1200,27 +1182,28 @@
},
{
"cell_type": "markdown",
- "id": "b31651f2",
"metadata": {},
"source": [
- "## Bitwise Operators\n",
+ "\n",
+ "## Bitwise Operators [
](#table_of_contents)\n",
+ "\n",
"\n",
"Bitwise operators treat operands as sequences of binary digits and operate on them bit by bit. Bitwise operators will be addressed fully later."
]
},
{
"cell_type": "markdown",
- "id": "ba596fec",
"metadata": {},
"source": [
- "## Identity Operators\n",
+ "\n",
+ "## Identity Operators [
](#table_of_contents)\n",
+ "\n",
"\n",
"Python provides two operators, `is` and `is not`, that determine whether the given operands have the same identity—that is, refer to the same object. This is not the same thing as equality, which means the two operands refer to objects that contain the same data but are not necessarily the same object."
]
},
{
"cell_type": "markdown",
- "id": "70c352df",
"metadata": {},
"source": [
"Here is an example of two object that are equal but not identical:"
@@ -1229,7 +1212,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "9ed4cf85",
"metadata": {},
"outputs": [],
"source": [
@@ -1240,7 +1222,6 @@
{
"cell_type": "code",
"execution_count": 65,
- "id": "beae0879",
"metadata": {},
"outputs": [
{
@@ -1258,7 +1239,6 @@
{
"cell_type": "code",
"execution_count": 66,
- "id": "19ba7b74",
"metadata": {},
"outputs": [
{
@@ -1279,7 +1259,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "58c101e2",
"metadata": {},
"outputs": [],
"source": [
@@ -1288,7 +1267,6 @@
},
{
"cell_type": "markdown",
- "id": "e70e17e7",
"metadata": {},
"source": [
"Here, `x` and `y` both refer to objects whose value is `1001`. They are equal. But they do not reference the same object, as you can verify:"
@@ -1297,7 +1275,6 @@
{
"cell_type": "code",
"execution_count": 68,
- "id": "79b3bffa",
"metadata": {},
"outputs": [
{
@@ -1318,7 +1295,6 @@
{
"cell_type": "code",
"execution_count": 69,
- "id": "116ea605",
"metadata": {},
"outputs": [
{
@@ -1338,7 +1314,6 @@
},
{
"cell_type": "markdown",
- "id": "b3743e65",
"metadata": {},
"source": [
"`x` and `y` do not have the same identity, and `x` is `y` returns `False`."
@@ -1346,7 +1321,6 @@
},
{
"cell_type": "markdown",
- "id": "7a85a8a4",
"metadata": {},
"source": [
"You saw previously that when you make an assignment like `x = y`, Python merely creates a second reference to the same object, and that you could confirm that fact with the `id()` function. You can also confirm it using the `is` operator:"
@@ -1355,7 +1329,6 @@
{
"cell_type": "code",
"execution_count": 112,
- "id": "7248d4c3",
"metadata": {},
"outputs": [],
"source": [
@@ -1366,7 +1339,6 @@
{
"cell_type": "code",
"execution_count": 113,
- "id": "eab98ef9",
"metadata": {},
"outputs": [
{
@@ -1387,7 +1359,6 @@
{
"cell_type": "code",
"execution_count": 114,
- "id": "697bee23",
"metadata": {},
"outputs": [
{
@@ -1408,7 +1379,6 @@
{
"cell_type": "code",
"execution_count": 115,
- "id": "c9e1462a",
"metadata": {},
"outputs": [
{
@@ -1429,7 +1399,6 @@
{
"cell_type": "code",
"execution_count": 123,
- "id": "53c450a3",
"metadata": {},
"outputs": [
{
@@ -1449,7 +1418,6 @@
},
{
"cell_type": "markdown",
- "id": "584a8306",
"metadata": {},
"source": [
"In this case, since `a` and `b` reference the same object, it stands to reason that `a` and `b` would be equal as well."
@@ -1457,7 +1425,6 @@
},
{
"cell_type": "markdown",
- "id": "df20743f",
"metadata": {},
"source": [
"Unsurprisingly, the opposite of `is` is `is not`:"
@@ -1466,7 +1433,6 @@
{
"cell_type": "code",
"execution_count": 75,
- "id": "f71d6191",
"metadata": {},
"outputs": [],
"source": [
@@ -1477,7 +1443,6 @@
{
"cell_type": "code",
"execution_count": 126,
- "id": "15356c11",
"metadata": {},
"outputs": [
{
@@ -1497,10 +1462,11 @@
},
{
"cell_type": "markdown",
- "id": "2a1251df",
"metadata": {},
"source": [
- "## Augmented Assignment Operators\n",
+ "\n",
+ "## Augmented Assignment Operators [
](#table_of_contents)\n",
+ "\n",
"\n",
"You have seen that a single equal sign (`=`) is used to assign a value to a variable. It is, of course, perfectly viable for the value to the right of the assignment to be an expression containing other variables:"
]
@@ -1508,7 +1474,6 @@
{
"cell_type": "code",
"execution_count": 186,
- "id": "70d83a70",
"metadata": {},
"outputs": [],
"source": [
@@ -1520,7 +1485,6 @@
{
"cell_type": "code",
"execution_count": 187,
- "id": "1446ee97",
"metadata": {},
"outputs": [
{
@@ -1540,7 +1504,6 @@
},
{
"cell_type": "markdown",
- "id": "7d2b4c96",
"metadata": {},
"source": [
"In fact, the expression to the right of the assignment can include references to the variable that is being assigned to:"
@@ -1548,7 +1511,6 @@
},
{
"cell_type": "markdown",
- "id": "29e02ba0",
"metadata": {},
"source": [
"Python supports a shorthand augmented assignment notation for these arithmetic and bitwise operators:\n",
@@ -1566,7 +1528,6 @@
},
{
"cell_type": "markdown",
- "id": "608cd54a",
"metadata": {},
"source": [
"For these operators, the following are equivalent:\n",
@@ -1579,7 +1540,6 @@
},
{
"cell_type": "markdown",
- "id": "81bb46ce",
"metadata": {},
"source": [
"Take a look at these examples:\n",
@@ -1593,16 +1553,17 @@
},
{
"cell_type": "markdown",
- "id": "47b56763",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"In this section, you learned about the diverse **operators** Python supports to combine objects into **expressions**."
]
},
{
"cell_type": "markdown",
- "id": "30d6c1eb",
"metadata": {},
"source": [
"Most of the examples you have seen so far have involved only simple atomic data, but you saw a brief introduction to the **string** data type. The next tutorial will explore **string** objects in much more detail."
@@ -1625,7 +1586,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/07 Strings.ipynb b/Python/01. Basics/07 Strings.ipynb
similarity index 90%
rename from python/01. Basics/07 Strings.ipynb
rename to Python/01. Basics/07 Strings.ipynb
index 875ebe7..a0d86c1 100755
--- a/python/01. Basics/07 Strings.ipynb
+++ b/Python/01. Basics/07 Strings.ipynb
@@ -2,30 +2,65 @@
"cells": [
{
"cell_type": "markdown",
- "id": "23184e8c",
"metadata": {},
"source": [
- "# Strings\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Strings \n",
"\n",
"In the section on Basic Data Types in Python, you learned how to define **strings**: objects that contain sequences of character data. Processing character data is integral to programming. It is a rare application that doesn’t need to manipulate strings at least to some extent."
]
},
{
"cell_type": "markdown",
- "id": "fa10c681",
"metadata": {},
"source": [
- "## String Manipulation\n",
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [String Manipulation](#string_manipulation)\n",
+ " * [String Operators](#string_operators)\n",
+ " * [The * Operator](#the_*_operator)\n",
+ " * [The in Operator](#the_in_operator)\n",
+ " * [Built-in String Functions](#built-in_string_functions)\n",
+ " * [`ord(c)`](#`ord(c)`)\n",
+ " * [`chr(n)`](#`chr(n)`)\n",
+ " * [len(s)](#len(s))\n",
+ " * [`str(obj)`](#`str(obj)`)\n",
+ "* [String Indexing](#string_indexing)\n",
+ "* [String Slicing](#string_slicing)\n",
+ " * [Specifying a Stride in a String Slice](#specifying_a_stride_in_a_string_slice)\n",
+ " * [Interpolating Variables Into a String](#interpolating_variables_into_a_string)\n",
+ " * [Modifying Strings](#modifying_strings)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## String Manipulation [
](#table_of_contents)\n",
+ "\n",
"\n",
"The sections below highlight the operators, methods, and functions that are available for working with strings."
]
},
{
"cell_type": "markdown",
- "id": "5f662515",
"metadata": {},
"source": [
- "### String Operators\n",
+ "\n",
+ "### String Operators [
](#table_of_contents)\n",
+ "\n",
"\n",
"You have already seen the operators `+` and `*` applied to numeric operands in the section on Operators and Expressions in Python. These two operators can be applied to strings as well."
]
@@ -33,7 +68,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "304f586c",
"metadata": {},
"outputs": [],
"source": [
@@ -45,7 +79,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "c19e1260",
"metadata": {},
"outputs": [
{
@@ -66,7 +99,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "390915d3",
"metadata": {},
"outputs": [
{
@@ -87,7 +119,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "4f7187bc",
"metadata": {},
"outputs": [
{
@@ -104,10 +135,11 @@
},
{
"cell_type": "markdown",
- "id": "9ae0c9fd",
"metadata": {},
"source": [
- "### The * Operator\n",
+ "\n",
+ "### The * Operator [
](#table_of_contents)\n",
+ "\n",
"\n",
"The `*` operator creates multiple copies of a string. If `s` is a string and `n` is an integer, either of the following expressions returns a string consisting of `n` concatenated copies of `s`:\n",
"\n",
@@ -119,7 +151,6 @@
},
{
"cell_type": "markdown",
- "id": "35fcc197",
"metadata": {},
"source": [
"Here are examples of both forms:"
@@ -128,7 +159,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "5fee7681",
"metadata": {},
"outputs": [],
"source": [
@@ -138,7 +168,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "a91d4e2c",
"metadata": {},
"outputs": [
{
@@ -159,7 +188,6 @@
{
"cell_type": "code",
"execution_count": 109,
- "id": "29b880af",
"metadata": {},
"outputs": [
{
@@ -179,7 +207,6 @@
},
{
"cell_type": "markdown",
- "id": "aa601859",
"metadata": {},
"source": [
"The multiplier operand `n` must be an integer. You’d think it would be required to be a positive integer, but amusingly, it can be zero or negative, in which case the result is an empty string:"
@@ -188,7 +215,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "eb8f0829",
"metadata": {},
"outputs": [
{
@@ -208,7 +234,6 @@
},
{
"cell_type": "markdown",
- "id": "94f37fc7",
"metadata": {},
"source": [
"If you were to create a string variable and initialize it to the empty string by assigning it the value `'foo' * -8`, anyone would rightly think you were a bit daft. But it would work."
@@ -216,10 +241,11 @@
},
{
"cell_type": "markdown",
- "id": "6c2dec9a",
"metadata": {},
"source": [
- "### The in Operator\n",
+ "\n",
+ "### The in Operator [
](#table_of_contents)\n",
+ "\n",
"\n",
"Python also provides a membership operator that can be used with strings. The in operator returns `True` if the first operand is contained within the second, and `False` otherwise:"
]
@@ -227,7 +253,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "aa92d506",
"metadata": {},
"outputs": [],
"source": [
@@ -237,7 +262,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "e9590c4e",
"metadata": {},
"outputs": [
{
@@ -258,7 +282,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "7fd79b34",
"metadata": {},
"outputs": [
{
@@ -278,7 +301,6 @@
},
{
"cell_type": "markdown",
- "id": "c46218b6",
"metadata": {},
"source": [
"There is also a `not in` operator, which does the opposite:"
@@ -287,7 +309,6 @@
{
"cell_type": "code",
"execution_count": 119,
- "id": "4b61a741",
"metadata": {},
"outputs": [
{
@@ -308,7 +329,6 @@
{
"cell_type": "code",
"execution_count": 34,
- "id": "bb36289f",
"metadata": {},
"outputs": [
{
@@ -328,10 +348,11 @@
},
{
"cell_type": "markdown",
- "id": "99da370e",
"metadata": {},
"source": [
- "### Built-in String Functions\n",
+ "\n",
+ "### Built-in String Functions [
](#table_of_contents)\n",
+ "\n",
"\n",
"As you saw in the tutorial on Basic Data Types in Python, Python provides many functions that are built-in to the interpreter and always available. Here are a few that work with strings:\n",
"\n",
@@ -346,7 +367,6 @@
},
{
"cell_type": "markdown",
- "id": "9c16daa2",
"metadata": {},
"source": [
"These are explored more fully below."
@@ -355,7 +375,6 @@
{
"cell_type": "code",
"execution_count": 126,
- "id": "065a5ffa",
"metadata": {},
"outputs": [
{
@@ -375,16 +394,17 @@
},
{
"cell_type": "markdown",
- "id": "144abae1",
"metadata": {},
"source": [
- "#### `ord(c)`\n",
+ "\n",
+ "#### `ord(c)` [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"> Returns an integer value for the given character."
]
},
{
"cell_type": "markdown",
- "id": "258d19cb",
"metadata": {},
"source": [
"At the most basic level, computers store all information as numbers. To represent character data, a translation scheme is used which maps each character to its representative number."
@@ -392,7 +412,6 @@
},
{
"cell_type": "markdown",
- "id": "e5aade2b",
"metadata": {},
"source": [
"The simplest scheme in common use is called [ASCII](https://en.wikipedia.org/wiki/ASCII). It covers the common Latin characters you are probably most accustomed to working with. For these characters, ord(c) returns the ASCII value for character c:"
@@ -401,7 +420,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "c27fbf59",
"metadata": {},
"outputs": [
{
@@ -422,7 +440,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "3a02c7cc",
"metadata": {},
"outputs": [
{
@@ -442,7 +459,6 @@
},
{
"cell_type": "markdown",
- "id": "7e8e3c5f",
"metadata": {},
"source": [
"ASCII is fine as far as it goes. But there are many different languages in use in the world and countless symbols and glyphs that appear in digital media. The full set of characters that potentially may need to be represented in computer code far surpasses the ordinary Latin letters, numbers, and symbols you usually see."
@@ -450,7 +466,6 @@
},
{
"cell_type": "markdown",
- "id": "b04c4073",
"metadata": {},
"source": [
"[Unicode](http://www.unicode.org/standard/WhatIsUnicode.html) is an ambitious standard that attempts to provide a numeric code for every possible character, in every possible language, on every possible platform. Python 3 supports Unicode extensively, including allowing Unicode characters within strings."
@@ -458,7 +473,6 @@
},
{
"cell_type": "markdown",
- "id": "4de6aad9",
"metadata": {},
"source": [
"As long as you stay in the domain of the common characters, there is little practical difference between ASCII and Unicode. But the `ord()` function will return numeric values for [Unicode characters](https://realpython.com/courses/python-unicode/) as well:"
@@ -467,7 +481,6 @@
{
"cell_type": "code",
"execution_count": 127,
- "id": "0cd71268",
"metadata": {},
"outputs": [
{
@@ -488,7 +501,6 @@
{
"cell_type": "code",
"execution_count": 128,
- "id": "c6b714d9",
"metadata": {},
"outputs": [
{
@@ -508,17 +520,17 @@
},
{
"cell_type": "markdown",
- "id": "e408ba9e",
"metadata": {},
"source": [
- "#### `chr(n)`\n",
+ "\n",
+ "#### `chr(n)` [
](#table_of_contents)\n",
+ "\n",
"\n",
"> Returns a character value for the given integer."
]
},
{
"cell_type": "markdown",
- "id": "334fa1cf",
"metadata": {},
"source": [
"`chr()` does the reverse of `ord()`. Given a numeric value `n`, `chr(n)` returns a string representing the character that corresponds to `n`:"
@@ -527,7 +539,6 @@
{
"cell_type": "code",
"execution_count": 133,
- "id": "ef2c4a45",
"metadata": {},
"outputs": [
{
@@ -548,7 +559,6 @@
{
"cell_type": "code",
"execution_count": 134,
- "id": "ada4d5f7",
"metadata": {},
"outputs": [
{
@@ -568,7 +578,6 @@
},
{
"cell_type": "markdown",
- "id": "3f7c454d",
"metadata": {},
"source": [
"`chr()` handles Unicode characters as well:"
@@ -577,7 +586,6 @@
{
"cell_type": "code",
"execution_count": 135,
- "id": "d80a064b",
"metadata": {},
"outputs": [
{
@@ -598,7 +606,6 @@
{
"cell_type": "code",
"execution_count": 136,
- "id": "80200ecb",
"metadata": {},
"outputs": [
{
@@ -618,10 +625,11 @@
},
{
"cell_type": "markdown",
- "id": "eb12b3f4",
"metadata": {},
"source": [
- "#### len(s)\n",
+ "\n",
+ "#### len(s) [
](#table_of_contents)\n",
+ "\n",
"\n",
"With `len()`, you can check Python string length. `len(s)` returns the number of characters in `s`:"
]
@@ -629,7 +637,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "37161877",
"metadata": {},
"outputs": [
{
@@ -650,10 +657,11 @@
},
{
"cell_type": "markdown",
- "id": "0cb9afd4",
"metadata": {},
"source": [
- "#### `str(obj)`\n",
+ "\n",
+ "#### `str(obj)` [
](#table_of_contents)\n",
+ "\n",
"\n",
"> Returns a string representation of an object."
]
@@ -661,7 +669,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "d2ba5dc5",
"metadata": {},
"outputs": [
{
@@ -681,7 +688,6 @@
},
{
"cell_type": "markdown",
- "id": "a45eae4a",
"metadata": {},
"source": [
"Virtually any object in Python can be rendered as a string. `str(obj)` returns the string representation of object obj:"
@@ -690,7 +696,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "8e7ba22f",
"metadata": {},
"outputs": [
{
@@ -711,7 +716,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "f9d91ce6",
"metadata": {},
"outputs": [
{
@@ -732,7 +736,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "427eb452",
"metadata": {},
"outputs": [
{
@@ -753,7 +756,6 @@
{
"cell_type": "code",
"execution_count": 146,
- "id": "7f80df9f",
"metadata": {},
"outputs": [
{
@@ -773,17 +775,17 @@
},
{
"cell_type": "markdown",
- "id": "0b114fd4",
"metadata": {},
"source": [
- "## String Indexing\n",
+ "\n",
+ "## String Indexing [
](#table_of_contents)\n",
+ "\n",
"\n",
"Often in programming languages, individual items in an ordered set of data can be accessed directly using a numeric index or key value. This process is referred to as indexing."
]
},
{
"cell_type": "markdown",
- "id": "d3318f34",
"metadata": {},
"source": [
"In Python, strings are ordered sequences of character data, and thus can be indexed in this way. Individual characters in a string can be accessed by specifying the string name followed by a number in square brackets (`[]`)."
@@ -791,7 +793,6 @@
},
{
"cell_type": "markdown",
- "id": "62417aa5",
"metadata": {},
"source": [
"String indexing in Python is zero-based: the first character in the string has index 0, the next has index 1, and so on. The index of the last character will be the length of the string minus one."
@@ -799,7 +800,6 @@
},
{
"cell_type": "markdown",
- "id": "8572a66d",
"metadata": {},
"source": [
"For example, a schematic diagram of the indices of the string 'foobar' would look like this:"
@@ -807,7 +807,6 @@
},
{
"cell_type": "markdown",
- "id": "19f6db4d",
"metadata": {},
"source": [
"
"
@@ -815,7 +814,6 @@
},
{
"cell_type": "markdown",
- "id": "0a8f26d4",
"metadata": {},
"source": [
"The individual characters can be accessed by index as follows:"
@@ -824,7 +822,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "eaf4fef3",
"metadata": {},
"outputs": [],
"source": [
@@ -834,7 +831,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "dbd0ce02",
"metadata": {},
"outputs": [
{
@@ -855,7 +851,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "c62afc05",
"metadata": {},
"outputs": [
{
@@ -876,7 +871,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "fb34188f",
"metadata": {},
"outputs": [
{
@@ -897,7 +891,6 @@
{
"cell_type": "code",
"execution_count": 32,
- "id": "67c96720",
"metadata": {},
"outputs": [
{
@@ -918,7 +911,6 @@
{
"cell_type": "code",
"execution_count": 33,
- "id": "964f4c3d",
"metadata": {},
"outputs": [
{
@@ -938,7 +930,6 @@
},
{
"cell_type": "markdown",
- "id": "d84b6e53",
"metadata": {},
"source": [
"Attempting to index beyond the end of the string results in an error:"
@@ -947,7 +938,6 @@
{
"cell_type": "code",
"execution_count": 65,
- "id": "29394047",
"metadata": {},
"outputs": [
{
@@ -968,7 +958,6 @@
},
{
"cell_type": "markdown",
- "id": "7c3045a1",
"metadata": {},
"source": [
"String indices can also be specified with negative numbers, in which case indexing occurs from the end of the string backward: `-1` refers to the last character, `-2` the second-to-last character, and so on. Here is the same diagram showing both the positive and negative indices into the string `'foobar'`:\n",
@@ -978,7 +967,6 @@
},
{
"cell_type": "markdown",
- "id": "fa634d72",
"metadata": {},
"source": [
"Here are some examples of negative indexing:"
@@ -987,7 +975,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "b926b461",
"metadata": {},
"outputs": [],
"source": [
@@ -997,7 +984,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "0dad316d",
"metadata": {},
"outputs": [
{
@@ -1018,7 +1004,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "a0fc50e7",
"metadata": {},
"outputs": [
{
@@ -1039,7 +1024,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "e33e707a",
"metadata": {},
"outputs": [
{
@@ -1060,7 +1044,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "4e774bd8",
"metadata": {},
"outputs": [
{
@@ -1080,7 +1063,6 @@
},
{
"cell_type": "markdown",
- "id": "e88d687d",
"metadata": {},
"source": [
"Attempting to index with negative numbers beyond the start of the string results in an error:"
@@ -1089,7 +1071,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "7b114e51",
"metadata": {},
"outputs": [
{
@@ -1110,7 +1091,6 @@
},
{
"cell_type": "markdown",
- "id": "fe12ee7a",
"metadata": {},
"source": [
"For any non-empty string `s`, `s[len(s)-1]` and `s[-1]` both return the last character. There isn’t any index that makes sense for an empty string."
@@ -1118,10 +1098,11 @@
},
{
"cell_type": "markdown",
- "id": "1d844f12",
"metadata": {},
"source": [
- "## String Slicing\n",
+ "\n",
+ "## String Slicing [
](#table_of_contents)\n",
+ "\n",
"\n",
"Python also allows a form of indexing syntax that extracts substrings from a string, known as string slicing. If `s` is a string, an expression of the form `s[m:n]` returns the portion of `s` starting with position `m`, and up to but not including position `n`:"
]
@@ -1129,7 +1110,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "dc1c8c8d",
"metadata": {},
"outputs": [],
"source": [
@@ -1139,7 +1119,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "9caac50f",
"metadata": {},
"outputs": [
{
@@ -1159,7 +1138,6 @@
},
{
"cell_type": "markdown",
- "id": "3c460a46",
"metadata": {},
"source": [
"> **Remember:** String indices are zero-based. The first character in a string has index `0`. This applies to both standard indexing and slicing."
@@ -1167,7 +1145,6 @@
},
{
"cell_type": "markdown",
- "id": "6bc29431",
"metadata": {},
"source": [
"Again, the second index specifies the first character that is not included in the result—the character `'r'` (`s[5]`) in the example above. That may seem slightly unintuitive, but it produces this result which makes sense: the expression `s[m:n]` will return a substring that is `n - m` characters in length, in this case, `5 - 2 = 3`."
@@ -1175,7 +1152,6 @@
},
{
"cell_type": "markdown",
- "id": "3c19e834",
"metadata": {},
"source": [
"If you omit the first index, the slice starts at the beginning of the string. Thus, `s[:m]` and `s[0:m]` are equivalent:"
@@ -1184,7 +1160,6 @@
{
"cell_type": "code",
"execution_count": 45,
- "id": "65e54921",
"metadata": {},
"outputs": [],
"source": [
@@ -1194,7 +1169,6 @@
{
"cell_type": "code",
"execution_count": 46,
- "id": "6a476644",
"metadata": {},
"outputs": [
{
@@ -1215,7 +1189,6 @@
{
"cell_type": "code",
"execution_count": 47,
- "id": "7ec12414",
"metadata": {},
"outputs": [
{
@@ -1235,7 +1208,6 @@
},
{
"cell_type": "markdown",
- "id": "4bb9b797",
"metadata": {},
"source": [
"Similarly, if you omit the second index as in `s[n:]`, the slice extends from the first index through the end of the string. This is a nice, concise alternative to the more cumbersome `s[n:len(s)]`:"
@@ -1244,7 +1216,6 @@
{
"cell_type": "code",
"execution_count": 48,
- "id": "72ef55f9",
"metadata": {},
"outputs": [],
"source": [
@@ -1254,7 +1225,6 @@
{
"cell_type": "code",
"execution_count": 49,
- "id": "be68a664",
"metadata": {},
"outputs": [
{
@@ -1275,7 +1245,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "a38390ee",
"metadata": {},
"outputs": [
{
@@ -1295,7 +1264,6 @@
},
{
"cell_type": "markdown",
- "id": "e02f081a",
"metadata": {},
"source": [
"For any string `s` and any integer `n` (`0 ≤ n ≤ len(s)`), `s[:n]` + `s[n:]` will be equal to `s`:"
@@ -1304,7 +1272,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "c783d1ba",
"metadata": {},
"outputs": [],
"source": [
@@ -1314,7 +1281,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "3a8bda17",
"metadata": {},
"outputs": [
{
@@ -1335,7 +1301,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "4287977f",
"metadata": {},
"outputs": [
{
@@ -1355,7 +1320,6 @@
},
{
"cell_type": "markdown",
- "id": "3d1c6c3b",
"metadata": {},
"source": [
"Omitting both indices returns the original string, in its entirety. Literally. It’s not a copy, it’s a reference to the original string:"
@@ -1364,7 +1328,6 @@
{
"cell_type": "code",
"execution_count": 139,
- "id": "99526ddd",
"metadata": {},
"outputs": [],
"source": [
@@ -1375,7 +1338,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "5935f149",
"metadata": {},
"outputs": [
{
@@ -1396,7 +1358,6 @@
{
"cell_type": "code",
"execution_count": 141,
- "id": "9f89d290",
"metadata": {},
"outputs": [
{
@@ -1417,7 +1378,6 @@
{
"cell_type": "code",
"execution_count": 122,
- "id": "f6b7252c",
"metadata": {},
"outputs": [
{
@@ -1437,7 +1397,6 @@
},
{
"cell_type": "markdown",
- "id": "1e2b086f",
"metadata": {},
"source": [
"If the first index in a slice is greater than or equal to the second index, Python returns an empty string. This is yet another obfuscated way to generate an empty string, in case you were looking for one:"
@@ -1446,7 +1405,6 @@
{
"cell_type": "code",
"execution_count": 123,
- "id": "2bd7e4d5",
"metadata": {},
"outputs": [
{
@@ -1467,7 +1425,6 @@
{
"cell_type": "code",
"execution_count": 124,
- "id": "416cac6b",
"metadata": {},
"outputs": [
{
@@ -1487,7 +1444,6 @@
},
{
"cell_type": "markdown",
- "id": "a4cfee25",
"metadata": {},
"source": [
"Negative indices can be used with slicing as well. `-1` refers to the last character, `-2` the second-to-last, and so on, just as with simple indexing. The diagram below shows how to slice the substring `'oob'` from the string `'foobar'` using both positive and negative indices:"
@@ -1495,7 +1451,6 @@
},
{
"cell_type": "markdown",
- "id": "e6534e11",
"metadata": {},
"source": [
"
"
@@ -1503,7 +1458,6 @@
},
{
"cell_type": "markdown",
- "id": "a5f795f0",
"metadata": {},
"source": [
"Here is the corresponding Python code:"
@@ -1512,7 +1466,6 @@
{
"cell_type": "code",
"execution_count": 97,
- "id": "ed873371",
"metadata": {},
"outputs": [],
"source": [
@@ -1522,7 +1475,6 @@
{
"cell_type": "code",
"execution_count": 98,
- "id": "8bada5f2",
"metadata": {},
"outputs": [
{
@@ -1543,7 +1495,6 @@
{
"cell_type": "code",
"execution_count": 99,
- "id": "bc825852",
"metadata": {},
"outputs": [
{
@@ -1564,7 +1515,6 @@
{
"cell_type": "code",
"execution_count": 100,
- "id": "da0b7858",
"metadata": {},
"outputs": [
{
@@ -1584,17 +1534,17 @@
},
{
"cell_type": "markdown",
- "id": "5d217458",
"metadata": {},
"source": [
- "### Specifying a Stride in a String Slice\n",
+ "\n",
+ "### Specifying a Stride in a String Slice [
](#table_of_contents)\n",
+ "\n",
"\n",
"There is one more variant of the slicing syntax to discuss. Adding an additional : and a third index designates a stride (also called a step), which indicates how many characters to jump after retrieving each character in the slice."
]
},
{
"cell_type": "markdown",
- "id": "131003a6",
"metadata": {},
"source": [
"For example, for the string `'foobar'`, the slice `0:6:2` starts with the first character and ends with the last character (the whole string), and every second character is skipped. This is shown in the following diagram:"
@@ -1602,7 +1552,6 @@
},
{
"cell_type": "markdown",
- "id": "5d1f9376",
"metadata": {},
"source": [
"
"
@@ -1610,7 +1559,6 @@
},
{
"cell_type": "markdown",
- "id": "cc84ca16",
"metadata": {},
"source": [
"Similarly, `1:6:2` specifies a slice starting with the second character (index 1) and ending with the last character, and again the stride value 2 causes every other character to be skipped:"
@@ -1618,7 +1566,6 @@
},
{
"cell_type": "markdown",
- "id": "74269e50",
"metadata": {},
"source": [
"
"
@@ -1626,7 +1573,6 @@
},
{
"cell_type": "markdown",
- "id": "5a71519e",
"metadata": {},
"source": [
"The illustrative REPL code is shown here:"
@@ -1635,7 +1581,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "d081aefb",
"metadata": {},
"outputs": [],
"source": [
@@ -1645,7 +1590,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "fac11ecc",
"metadata": {},
"outputs": [
{
@@ -1666,7 +1610,6 @@
{
"cell_type": "code",
"execution_count": 150,
- "id": "511dfc17",
"metadata": {},
"outputs": [
{
@@ -1686,7 +1629,6 @@
},
{
"cell_type": "markdown",
- "id": "8eb3fb70",
"metadata": {},
"source": [
"As with any slicing, the first and second indices can be omitted, and default to the first and last characters respectively:"
@@ -1695,7 +1637,6 @@
{
"cell_type": "code",
"execution_count": 69,
- "id": "9d29edff",
"metadata": {},
"outputs": [
{
@@ -1717,7 +1658,6 @@
{
"cell_type": "code",
"execution_count": 70,
- "id": "0ecd60d3",
"metadata": {},
"outputs": [
{
@@ -1738,7 +1678,6 @@
{
"cell_type": "code",
"execution_count": 71,
- "id": "bf177775",
"metadata": {},
"outputs": [
{
@@ -1758,7 +1697,6 @@
},
{
"cell_type": "markdown",
- "id": "8840ec22",
"metadata": {},
"source": [
"You can specify a negative stride value as well, in which case Python steps backward through the string. In that case, the starting/first index should be greater than the ending/second index:"
@@ -1767,7 +1705,6 @@
{
"cell_type": "code",
"execution_count": 73,
- "id": "33b0d776",
"metadata": {},
"outputs": [],
"source": [
@@ -1777,7 +1714,6 @@
{
"cell_type": "code",
"execution_count": 74,
- "id": "8369d371",
"metadata": {},
"outputs": [
{
@@ -1797,7 +1733,6 @@
},
{
"cell_type": "markdown",
- "id": "29eb0e9c",
"metadata": {},
"source": [
"In the above example, `5:0:-2` means “start at the last character and step backward by 2, up to but not including the first character.”"
@@ -1805,7 +1740,6 @@
},
{
"cell_type": "markdown",
- "id": "f4881569",
"metadata": {},
"source": [
"When you are stepping backward, if the first and second indices are omitted, the defaults are reversed in an intuitive way: the first index defaults to the end of the string, and the second index defaults to the beginning. Here is an example:"
@@ -1814,7 +1748,6 @@
{
"cell_type": "code",
"execution_count": 79,
- "id": "2695ac21",
"metadata": {},
"outputs": [
{
@@ -1836,7 +1769,6 @@
{
"cell_type": "code",
"execution_count": 80,
- "id": "c61e970d",
"metadata": {},
"outputs": [
{
@@ -1856,7 +1788,6 @@
},
{
"cell_type": "markdown",
- "id": "a693b6e0",
"metadata": {},
"source": [
"This is a common paradigm for reversing a string:"
@@ -1865,7 +1796,6 @@
{
"cell_type": "code",
"execution_count": 81,
- "id": "60613308",
"metadata": {},
"outputs": [
{
@@ -1886,17 +1816,17 @@
},
{
"cell_type": "markdown",
- "id": "79e56be3",
"metadata": {},
"source": [
- "### Interpolating Variables Into a String\n",
+ "\n",
+ "### Interpolating Variables Into a String [
](#table_of_contents)\n",
+ "\n",
"\n",
"In Python version 3.6, a new string formatting mechanism was introduced. This feature is formally named the Formatted String Literal, but is more usually referred to by its nickname **f-string**."
]
},
{
"cell_type": "markdown",
- "id": "77adf220",
"metadata": {},
"source": [
"One simple feature of f-strings you can start using right away is variable interpolation. You can specify a variable name directly within an f-string literal, and Python will replace the name with the corresponding value."
@@ -1904,7 +1834,6 @@
},
{
"cell_type": "markdown",
- "id": "38ca79f3",
"metadata": {},
"source": [
"For example, suppose you want to display the result of an arithmetic calculation. You can do this with a straightforward `print()` statement, separating numeric values and string literals by commas:"
@@ -1913,7 +1842,6 @@
{
"cell_type": "code",
"execution_count": 173,
- "id": "4ef35286",
"metadata": {},
"outputs": [],
"source": [
@@ -1925,7 +1853,6 @@
{
"cell_type": "code",
"execution_count": 174,
- "id": "0e7c2ac3",
"metadata": {},
"outputs": [
{
@@ -1942,7 +1869,6 @@
},
{
"cell_type": "markdown",
- "id": "b9400efe",
"metadata": {},
"source": [
"But this is cumbersome. To accomplish the same thing using an f-string:\n",
@@ -1953,7 +1879,6 @@
},
{
"cell_type": "markdown",
- "id": "a300882d",
"metadata": {},
"source": [
"Recast using an f-string, the above example looks much cleaner:"
@@ -1962,7 +1887,6 @@
{
"cell_type": "code",
"execution_count": 182,
- "id": "1476ff7f",
"metadata": {},
"outputs": [],
"source": [
@@ -1974,7 +1898,6 @@
{
"cell_type": "code",
"execution_count": 181,
- "id": "8981e5fd",
"metadata": {},
"outputs": [
{
@@ -1991,7 +1914,6 @@
},
{
"cell_type": "markdown",
- "id": "2a58dedc",
"metadata": {},
"source": [
"Any of Python’s three quoting mechanisms can be used to define an f-string:"
@@ -2000,7 +1922,6 @@
{
"cell_type": "code",
"execution_count": 131,
- "id": "a622099d",
"metadata": {},
"outputs": [],
"source": [
@@ -2010,7 +1931,6 @@
{
"cell_type": "code",
"execution_count": 132,
- "id": "a6b78d81",
"metadata": {},
"outputs": [
{
@@ -2028,7 +1948,6 @@
{
"cell_type": "code",
"execution_count": 133,
- "id": "85026c41",
"metadata": {},
"outputs": [
{
@@ -2046,7 +1965,6 @@
{
"cell_type": "code",
"execution_count": 134,
- "id": "086a85df",
"metadata": {},
"outputs": [
{
@@ -2063,17 +1981,17 @@
},
{
"cell_type": "markdown",
- "id": "ddd555bf",
"metadata": {},
"source": [
- "### Modifying Strings\n",
+ "\n",
+ "### Modifying Strings [
](#table_of_contents)\n",
+ "\n",
"\n",
"In a nutshell, you can’t. Strings are one of the data types Python considers immutable, meaning not able to be changed. In fact, all the data types you have seen so far are immutable. (Python does provide data types that are mutable, as you will soon see.)"
]
},
{
"cell_type": "markdown",
- "id": "b4be2765",
"metadata": {},
"source": [
"A statement like this will cause an error:"
@@ -2082,7 +2000,6 @@
{
"cell_type": "code",
"execution_count": 191,
- "id": "9f475df5",
"metadata": {},
"outputs": [],
"source": [
@@ -2092,7 +2009,6 @@
{
"cell_type": "code",
"execution_count": 193,
- "id": "3df90174",
"metadata": {},
"outputs": [
{
@@ -2113,7 +2029,6 @@
},
{
"cell_type": "markdown",
- "id": "857cda55",
"metadata": {},
"source": [
"In truth, there really isn’t much need to modify strings. You can usually easily accomplish what you want by generating a copy of the original string that has the desired change in place. There are very many ways to do this in Python. Here is one possibility:"
@@ -2122,7 +2037,6 @@
{
"cell_type": "code",
"execution_count": 93,
- "id": "066fbc51",
"metadata": {},
"outputs": [],
"source": [
@@ -2132,7 +2046,6 @@
{
"cell_type": "code",
"execution_count": 94,
- "id": "78dd04d6",
"metadata": {},
"outputs": [
{
@@ -2152,16 +2065,17 @@
},
{
"cell_type": "markdown",
- "id": "2b2f1422",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"This tutorial provided an in-depth look at the many different mechanisms Python provides for string handling, including string operators, built-in functions, indexing, slicing, and built-in methods. You also were introduced to the bytes and bytearray types."
]
},
{
"cell_type": "markdown",
- "id": "04c7a670",
"metadata": {},
"source": [
"These types are the first types you have examined that are composite—built from a collection of smaller parts. Python provides several composite built-in types. In the next tutorial, you will explore two of the most frequently used: `lists` and `tuples`."
@@ -2184,7 +2098,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/08 Composite Data Types: Lists and Tuples.ipynb b/Python/01. Basics/08 Composite Data Types: Lists and Tuples.ipynb
similarity index 92%
rename from python/01. Basics/08 Composite Data Types: Lists and Tuples.ipynb
rename to Python/01. Basics/08 Composite Data Types: Lists and Tuples.ipynb
index 2bd8ae5..20a26a9 100755
--- a/python/01. Basics/08 Composite Data Types: Lists and Tuples.ipynb
+++ b/Python/01. Basics/08 Composite Data Types: Lists and Tuples.ipynb
@@ -2,11 +2,48 @@
"cells": [
{
"cell_type": "markdown",
- "id": "5731f133",
"metadata": {},
"source": [
- "# Lists and Tuples in Python\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Lists and Tuples in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Python Lists](#python_lists)\n",
+ " * [1. Lists Are Ordered](#1._lists_are_ordered)\n",
+ " * [2. Lists Can Contain Arbitrary Objects](#2._lists_can_contain_arbitrary_objects)\n",
+ " * [3. List Elements Can Be Accessed by Index](#3._list_elements_can_be_accessed_by_index)\n",
+ " * [4. Lists Can Be Nested](#4._lists_can_be_nested)\n",
+ " * [5. Lists Are Mutable](#5._lists_are_mutable)\n",
+ " * [Modifying a Single List Value](#modifying_a_single_list_value)\n",
+ " * [Modifying Multiple List Values](#modifying_multiple_list_values)\n",
+ " * [Prepending or Appending Items to a List](#prepending_or_appending_items_to_a_list)\n",
+ " * [6. Lists Are Dynamic](#6._lists_are_dynamic)\n",
+ "* [Python Tuples](#python_tuples)\n",
+ " * [Defining and Using Tuples](#defining_and_using_tuples)\n",
+ " * [Tuple Assignment, Packing, and Unpacking](#tuple_assignment,_packing,_and_unpacking)\n",
+ "* [
Conclusion](#conclusion)\n",
"\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
"Lists and tuples are arguably Python’s most versatile, useful data types. You will find them in virtually every nontrivial Python program.\n",
"\n",
"* [Python Lists](#lists)\n",
@@ -15,18 +52,18 @@
},
{
"cell_type": "markdown",
- "id": "f28d785b",
"metadata": {},
"source": [
- "\n",
- "## Python Lists\n",
+ "\n",
+ "## Python Lists [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"In short, a list is a collection of arbitrary objects, somewhat akin to an array in many other programming languages but more flexible. Lists are defined in Python by enclosing a comma-separated sequence of objects in square brackets (`[]`), as shown below:"
]
},
{
"cell_type": "code",
"execution_count": 3,
- "id": "224fa3d1",
"metadata": {},
"outputs": [],
"source": [
@@ -36,7 +73,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "944b8e8e",
"metadata": {},
"outputs": [
{
@@ -56,7 +92,6 @@
},
{
"cell_type": "markdown",
- "id": "9a664dba",
"metadata": {},
"source": [
"The important characteristics of Python lists are as follows:\n",
@@ -73,10 +108,11 @@
},
{
"cell_type": "markdown",
- "id": "8fcc63ff",
"metadata": {},
"source": [
- "### 1. Lists Are Ordered\n",
+ "\n",
+ "### 1. Lists Are Ordered [
](#table_of_contents)\n",
+ "\n",
"\n",
"A list is not merely a collection of objects. It is an ordered collection of objects. The order in which you specify the elements when you define a list is an innate characteristic of that list and is maintained for that list’s lifetime. (You will see a Python data type that is not ordered in the next tutorial on dictionaries.)"
]
@@ -84,7 +120,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "53905c3b",
"metadata": {},
"outputs": [
{
@@ -107,7 +142,6 @@
{
"cell_type": "code",
"execution_count": 23,
- "id": "f39383c3",
"metadata": {},
"outputs": [
{
@@ -130,7 +164,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "781d0d7f",
"metadata": {},
"outputs": [
{
@@ -152,7 +185,6 @@
},
{
"cell_type": "markdown",
- "id": "edef844e",
"metadata": {},
"source": [
"Lists that have the same elements in a different order are not the same:"
@@ -161,7 +193,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "72304bd7",
"metadata": {},
"outputs": [
{
@@ -184,7 +215,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "3260c4af",
"metadata": {},
"outputs": [
{
@@ -205,7 +235,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "e15600e5",
"metadata": {},
"outputs": [
{
@@ -225,10 +254,11 @@
},
{
"cell_type": "markdown",
- "id": "0730d1ba",
"metadata": {},
"source": [
- "### 2. Lists Can Contain Arbitrary Objects\n",
+ "\n",
+ "### 2. Lists Can Contain Arbitrary Objects [
](#table_of_contents)\n",
+ "\n",
"\n",
"A list can contain any assortment of objects. The elements of a list can all be the same type:"
]
@@ -236,7 +266,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "ecb1aea3",
"metadata": {},
"outputs": [],
"source": [
@@ -245,7 +274,6 @@
},
{
"cell_type": "markdown",
- "id": "b649d22a",
"metadata": {},
"source": [
"Or the elements can be of varying types:"
@@ -254,7 +282,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "bc2d44ac",
"metadata": {},
"outputs": [],
"source": [
@@ -263,7 +290,6 @@
},
{
"cell_type": "markdown",
- "id": "c3dea005",
"metadata": {},
"source": [
"Lists can even contain complex objects, like functions, classes, and modules, which you will learn about in upcoming sections:"
@@ -272,7 +298,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "163b0ecc",
"metadata": {},
"outputs": [
{
@@ -293,7 +318,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "e60eef24",
"metadata": {},
"outputs": [
{
@@ -314,7 +338,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "0aff05d4",
"metadata": {},
"outputs": [],
"source": [
@@ -325,7 +348,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "3cea8915",
"metadata": {},
"outputs": [
{
@@ -346,7 +368,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "4eb83630",
"metadata": {},
"outputs": [
{
@@ -368,7 +389,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "3e5e59ca",
"metadata": {},
"outputs": [],
"source": [
@@ -378,7 +398,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "128d97e1",
"metadata": {},
"outputs": [
{
@@ -401,7 +420,6 @@
},
{
"cell_type": "markdown",
- "id": "778f7f8e",
"metadata": {},
"source": [
"A list can contain any number of objects, from zero to as many as your computer’s memory will allow:"
@@ -410,7 +428,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "084b9669",
"metadata": {},
"outputs": [
{
@@ -432,7 +449,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "8f6f663e",
"metadata": {},
"outputs": [
{
@@ -454,7 +470,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "afd84114",
"metadata": {},
"outputs": [],
"source": [
@@ -467,7 +482,6 @@
},
{
"cell_type": "markdown",
- "id": "fbd823f5",
"metadata": {},
"source": [
"> A list with a single object is sometimes referred to as a singleton list."
@@ -475,7 +489,6 @@
},
{
"cell_type": "markdown",
- "id": "83ed1c59",
"metadata": {},
"source": [
"List objects needn’t be unique. A given object can appear in a list multiple times:\n",
@@ -485,7 +498,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "1e372e7d",
"metadata": {},
"outputs": [],
"source": [
@@ -494,17 +506,17 @@
},
{
"cell_type": "markdown",
- "id": "f363f673",
"metadata": {},
"source": [
- "### 3. List Elements Can Be Accessed by Index\n",
+ "\n",
+ "### 3. List Elements Can Be Accessed by Index [
](#table_of_contents)\n",
+ "\n",
"\n",
"Individual elements in a list can be accessed using an index in square brackets. This is exactly analogous to accessing individual characters in a string. List indexing is zero-based as it is with strings."
]
},
{
"cell_type": "markdown",
- "id": "75516647",
"metadata": {},
"source": [
"Consider the following list:"
@@ -513,7 +525,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "1238b74b",
"metadata": {},
"outputs": [],
"source": [
@@ -522,7 +533,6 @@
},
{
"cell_type": "markdown",
- "id": "68b1c05e",
"metadata": {},
"source": [
"The indices for the elements in a are shown below:"
@@ -530,7 +540,6 @@
},
{
"cell_type": "markdown",
- "id": "76be95f6",
"metadata": {},
"source": [
"
"
@@ -538,7 +547,6 @@
},
{
"cell_type": "markdown",
- "id": "daa07526",
"metadata": {},
"source": [
"Here is Python code to access some elements of a:"
@@ -547,7 +555,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "448bffb0",
"metadata": {},
"outputs": [
{
@@ -568,7 +575,6 @@
{
"cell_type": "code",
"execution_count": 45,
- "id": "ca66f33a",
"metadata": {},
"outputs": [
{
@@ -589,7 +595,6 @@
{
"cell_type": "code",
"execution_count": 46,
- "id": "ce6610ee",
"metadata": {},
"outputs": [
{
@@ -609,7 +614,6 @@
},
{
"cell_type": "markdown",
- "id": "5806ee80",
"metadata": {},
"source": [
"Virtually everything about string indexing works similarly for lists. For example, a negative list index counts from the end of the list:"
@@ -617,7 +621,6 @@
},
{
"cell_type": "markdown",
- "id": "f4e80df9",
"metadata": {},
"source": [
"
"
@@ -626,7 +629,6 @@
{
"cell_type": "code",
"execution_count": 47,
- "id": "f471f429",
"metadata": {},
"outputs": [
{
@@ -647,7 +649,6 @@
{
"cell_type": "code",
"execution_count": 48,
- "id": "d71d5047",
"metadata": {},
"outputs": [
{
@@ -668,7 +669,6 @@
{
"cell_type": "code",
"execution_count": 49,
- "id": "9a9c8dc6",
"metadata": {},
"outputs": [
{
@@ -688,7 +688,6 @@
},
{
"cell_type": "markdown",
- "id": "24e25038",
"metadata": {},
"source": [
"Slicing also works. If a is a list, the expression `a[m:n]` returns the portion of a from index `m` to, but not including, index `n`:"
@@ -697,7 +696,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "2470be05",
"metadata": {},
"outputs": [],
"source": [
@@ -707,7 +705,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "ec76a7a8",
"metadata": {},
"outputs": [
{
@@ -727,7 +724,6 @@
},
{
"cell_type": "markdown",
- "id": "92e8b2b7",
"metadata": {},
"source": [
"Other features of string slicing work analogously for list slicing as well:\n",
@@ -738,7 +734,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "186bff88",
"metadata": {},
"outputs": [
{
@@ -759,7 +754,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "2be72101",
"metadata": {},
"outputs": [
{
@@ -780,7 +774,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "3271375e",
"metadata": {},
"outputs": [
{
@@ -800,7 +793,6 @@
},
{
"cell_type": "markdown",
- "id": "e4ec255c",
"metadata": {},
"source": [
"- Omitting the first index starts the slice at the beginning of the list, and omitting the second index extends the slice to the end of the list:"
@@ -809,7 +801,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "f0245a1e",
"metadata": {},
"outputs": [
{
@@ -830,7 +821,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "be388ba4",
"metadata": {},
"outputs": [
{
@@ -851,7 +841,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "ca046fc9",
"metadata": {},
"outputs": [
{
@@ -872,7 +861,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "49b8dc08",
"metadata": {},
"outputs": [
{
@@ -892,7 +880,6 @@
},
{
"cell_type": "markdown",
- "id": "6e2815b6",
"metadata": {},
"source": [
"- You can specify a stride—either positive or negative:"
@@ -901,7 +888,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "29c7bfe1",
"metadata": {},
"outputs": [
{
@@ -922,7 +908,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "079d8938",
"metadata": {},
"outputs": [
{
@@ -943,7 +928,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "b9dbfed7",
"metadata": {},
"outputs": [
{
@@ -963,7 +947,6 @@
},
{
"cell_type": "markdown",
- "id": "bb2d086b",
"metadata": {},
"source": [
"- The syntax for reversing a list works the same way it does for strings:"
@@ -972,7 +955,6 @@
{
"cell_type": "code",
"execution_count": 23,
- "id": "ea5539a8",
"metadata": {},
"outputs": [
{
@@ -992,7 +974,6 @@
},
{
"cell_type": "markdown",
- "id": "2a2024a9",
"metadata": {},
"source": [
"The `[:]` syntax works for lists. However, there is an important difference between how this operation works with a list and how it works with a string."
@@ -1001,7 +982,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "67e73393",
"metadata": {},
"outputs": [],
"source": [
@@ -1011,7 +991,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "730ff483",
"metadata": {},
"outputs": [
{
@@ -1032,7 +1011,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "5e2ab6a8",
"metadata": {},
"outputs": [
{
@@ -1052,7 +1030,6 @@
},
{
"cell_type": "markdown",
- "id": "f00040e0",
"metadata": {},
"source": [
"Conversely, if a is a list, `a[:]` returns a new object that is a copy of `a`:"
@@ -1061,7 +1038,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "f80028f3",
"metadata": {},
"outputs": [],
"source": [
@@ -1071,7 +1047,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "cb40d715",
"metadata": {},
"outputs": [
{
@@ -1092,7 +1067,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "e3bf2d76",
"metadata": {},
"outputs": [
{
@@ -1112,7 +1086,6 @@
},
{
"cell_type": "markdown",
- "id": "fcf87db3",
"metadata": {},
"source": [
"Several Python operators and built-in functions can also be used with lists in ways that are analogous to strings:"
@@ -1120,7 +1093,6 @@
},
{
"cell_type": "markdown",
- "id": "bfbe8474",
"metadata": {},
"source": [
"- The `in` and `not in` operators:"
@@ -1129,7 +1101,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "5d0cc53f",
"metadata": {},
"outputs": [
{
@@ -1150,7 +1121,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "5175dcf1",
"metadata": {},
"outputs": [
{
@@ -1170,7 +1140,6 @@
},
{
"cell_type": "markdown",
- "id": "baa92b09",
"metadata": {},
"source": [
"- The concatenation (`+`) and replication (`*`) operators:"
@@ -1179,7 +1148,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "da5445ce",
"metadata": {},
"outputs": [
{
@@ -1200,7 +1168,6 @@
{
"cell_type": "code",
"execution_count": 62,
- "id": "8f052203",
"metadata": {},
"outputs": [
{
@@ -1221,7 +1188,6 @@
{
"cell_type": "code",
"execution_count": 63,
- "id": "e975dda3",
"metadata": {},
"outputs": [
{
@@ -1252,7 +1218,6 @@
},
{
"cell_type": "markdown",
- "id": "477f6546",
"metadata": {},
"source": [
"- The `len()`, `min()`, and `max(`) functions:"
@@ -1261,7 +1226,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "8f61ce86",
"metadata": {},
"outputs": [
{
@@ -1282,7 +1246,6 @@
{
"cell_type": "code",
"execution_count": 65,
- "id": "19230651",
"metadata": {},
"outputs": [
{
@@ -1303,7 +1266,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "a1465ad4",
"metadata": {},
"outputs": [
{
@@ -1324,7 +1286,6 @@
{
"cell_type": "code",
"execution_count": 70,
- "id": "73689bec",
"metadata": {},
"outputs": [
{
@@ -1344,7 +1305,6 @@
},
{
"cell_type": "markdown",
- "id": "5bd17e5b",
"metadata": {},
"source": [
"It’s not an accident that strings and lists behave so similarly. They are both special cases of a more general object type called an iterable, which you will encounter in more detail in the upcoming tutorial on definite iteration."
@@ -1352,7 +1312,6 @@
},
{
"cell_type": "markdown",
- "id": "5024671e",
"metadata": {},
"source": [
"By the way, in each example above, the list is always assigned to a variable before an operation is performed on it. But you can operate on a list literal as well:"
@@ -1361,7 +1320,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "2e58770c",
"metadata": {},
"outputs": [
{
@@ -1382,7 +1340,6 @@
{
"cell_type": "code",
"execution_count": 41,
- "id": "349a8106",
"metadata": {},
"outputs": [
{
@@ -1403,7 +1360,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "6f28b0fd",
"metadata": {},
"outputs": [
{
@@ -1424,7 +1380,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "2224aa12",
"metadata": {},
"outputs": [
{
@@ -1445,7 +1400,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "097e60a7",
"metadata": {},
"outputs": [
{
@@ -1465,7 +1419,6 @@
},
{
"cell_type": "markdown",
- "id": "0661e9b1",
"metadata": {},
"source": [
"For that matter, you can do likewise with a string literal:"
@@ -1474,7 +1427,6 @@
{
"cell_type": "code",
"execution_count": 124,
- "id": "bf6c14f6",
"metadata": {},
"outputs": [
{
@@ -1494,17 +1446,17 @@
},
{
"cell_type": "markdown",
- "id": "825e5a91",
"metadata": {},
"source": [
- "### 4. Lists Can Be Nested\n",
+ "\n",
+ "### 4. Lists Can Be Nested [
](#table_of_contents)\n",
+ "\n",
"\n",
"You have seen that an element in a list can be any sort of object. That includes another list. A list can contain sublists, which in turn can contain sublists themselves, and so on to arbitrary depth."
]
},
{
"cell_type": "markdown",
- "id": "f2c80983",
"metadata": {},
"source": [
"Consider this (admittedly contrived) example:"
@@ -1513,7 +1465,6 @@
{
"cell_type": "code",
"execution_count": 72,
- "id": "509d8e21",
"metadata": {},
"outputs": [],
"source": [
@@ -1522,7 +1473,6 @@
},
{
"cell_type": "markdown",
- "id": "ff778fc9",
"metadata": {},
"source": [
"The object structure that x references is diagrammed below:"
@@ -1530,7 +1480,6 @@
},
{
"cell_type": "markdown",
- "id": "7b681953",
"metadata": {},
"source": [
"
"
@@ -1538,7 +1487,6 @@
},
{
"cell_type": "markdown",
- "id": "cf3b3523",
"metadata": {},
"source": [
"`x[0]`, `x[2]`, and `x[4]` are strings, each one character long:"
@@ -1547,7 +1495,6 @@
{
"cell_type": "code",
"execution_count": 49,
- "id": "f7d30716",
"metadata": {},
"outputs": [
{
@@ -1567,7 +1514,6 @@
},
{
"cell_type": "markdown",
- "id": "faa8b1de",
"metadata": {},
"source": [
"But `x[1]` and `x[3]` are sublists:"
@@ -1576,7 +1522,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "bd2afbad",
"metadata": {},
"outputs": [
{
@@ -1597,7 +1542,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "9b0bbf8d",
"metadata": {},
"outputs": [
{
@@ -1617,7 +1561,6 @@
},
{
"cell_type": "markdown",
- "id": "14b8a84b",
"metadata": {},
"source": [
"To access the items in a sublist, simply append an additional index:"
@@ -1626,7 +1569,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "e539e131",
"metadata": {},
"outputs": [
{
@@ -1647,7 +1589,6 @@
{
"cell_type": "code",
"execution_count": 54,
- "id": "7ffdb3b7",
"metadata": {},
"outputs": [
{
@@ -1668,7 +1609,6 @@
{
"cell_type": "code",
"execution_count": 55,
- "id": "3189540d",
"metadata": {},
"outputs": [
{
@@ -1689,7 +1629,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "a22d1a11",
"metadata": {},
"outputs": [
{
@@ -1710,7 +1649,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "61b051cf",
"metadata": {},
"outputs": [
{
@@ -1731,7 +1669,6 @@
{
"cell_type": "code",
"execution_count": 58,
- "id": "008ef48d",
"metadata": {},
"outputs": [
{
@@ -1751,7 +1688,6 @@
},
{
"cell_type": "markdown",
- "id": "2e8c0f2d",
"metadata": {},
"source": [
"`x[1][1]` is yet another sublist, so adding one more index accesses its elements:"
@@ -1760,7 +1696,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "e21d0bfa",
"metadata": {},
"outputs": [
{
@@ -1781,7 +1716,6 @@
{
"cell_type": "code",
"execution_count": 60,
- "id": "1089635f",
"metadata": {},
"outputs": [
{
@@ -1802,7 +1736,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "c4f7356c",
"metadata": {},
"outputs": [
{
@@ -1822,7 +1755,6 @@
},
{
"cell_type": "markdown",
- "id": "ba86c2fe",
"metadata": {},
"source": [
"There is no limit, short of the extent of your computer’s memory, to the depth or complexity with which lists can be nested in this way."
@@ -1830,7 +1762,6 @@
},
{
"cell_type": "markdown",
- "id": "8eb1e152",
"metadata": {},
"source": [
"All the usual syntax regarding indices and slicing applies to sublists as well:"
@@ -1839,7 +1770,6 @@
{
"cell_type": "code",
"execution_count": 62,
- "id": "67b5631b",
"metadata": {},
"outputs": [
{
@@ -1860,7 +1790,6 @@
{
"cell_type": "code",
"execution_count": 63,
- "id": "4f9714c4",
"metadata": {},
"outputs": [
{
@@ -1881,7 +1810,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "81a1dd51",
"metadata": {},
"outputs": [
{
@@ -1901,7 +1829,6 @@
},
{
"cell_type": "markdown",
- "id": "54539219",
"metadata": {},
"source": [
"However, be aware that operators and functions apply to only the list at the level you specify and are not recursive. Consider what happens when you query the length of `x` using `len()`:"
@@ -1910,7 +1837,6 @@
{
"cell_type": "code",
"execution_count": 65,
- "id": "3e78a3f3",
"metadata": {},
"outputs": [
{
@@ -1931,7 +1857,6 @@
{
"cell_type": "code",
"execution_count": 66,
- "id": "9b4e8070",
"metadata": {},
"outputs": [
{
@@ -1952,7 +1877,6 @@
{
"cell_type": "code",
"execution_count": 67,
- "id": "0d39cd2b",
"metadata": {},
"outputs": [
{
@@ -1973,7 +1897,6 @@
{
"cell_type": "code",
"execution_count": 68,
- "id": "46a1e57f",
"metadata": {},
"outputs": [
{
@@ -1994,7 +1917,6 @@
{
"cell_type": "code",
"execution_count": 69,
- "id": "2bc7ba3b",
"metadata": {},
"outputs": [
{
@@ -2015,7 +1937,6 @@
{
"cell_type": "code",
"execution_count": 70,
- "id": "e35f116f",
"metadata": {},
"outputs": [
{
@@ -2036,7 +1957,6 @@
{
"cell_type": "code",
"execution_count": 71,
- "id": "32578a6b",
"metadata": {},
"outputs": [
{
@@ -2056,7 +1976,6 @@
},
{
"cell_type": "markdown",
- "id": "c1afc28b",
"metadata": {},
"source": [
"`x` has only five elements—three strings and two sublists. The individual elements in the sublists don’t count toward `x`’s length."
@@ -2064,7 +1983,6 @@
},
{
"cell_type": "markdown",
- "id": "73e857bf",
"metadata": {},
"source": [
"You’d encounter a similar situation when using the in operator:"
@@ -2073,7 +1991,6 @@
{
"cell_type": "code",
"execution_count": 72,
- "id": "70d8feb6",
"metadata": {},
"outputs": [
{
@@ -2094,7 +2011,6 @@
{
"cell_type": "code",
"execution_count": 73,
- "id": "4b79b4a8",
"metadata": {},
"outputs": [
{
@@ -2115,7 +2031,6 @@
{
"cell_type": "code",
"execution_count": 74,
- "id": "0d691d61",
"metadata": {},
"outputs": [
{
@@ -2135,7 +2050,6 @@
},
{
"cell_type": "markdown",
- "id": "0f83d93c",
"metadata": {},
"source": [
"`'ddd'` is not one of the elements in `x` or `x[1]`. It is only directly an element in the sublist `x[1][1]`. An individual element in a sublist does not count as an element of the parent `list(s)`."
@@ -2143,16 +2057,17 @@
},
{
"cell_type": "markdown",
- "id": "ea5e4006",
"metadata": {},
"source": [
- "### 5. Lists Are Mutable\n",
+ "\n",
+ "### 5. Lists Are Mutable [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"Most of the data types you have encountered so far have been atomic types. Integer or float objects, for example, are primitive units that can’t be further broken down. These types are immutable, meaning that they can’t be changed once they have been assigned. It doesn’t make much sense to think of changing the value of an integer. If you want a different integer, you just assign a different one."
]
},
{
"cell_type": "markdown",
- "id": "2034dc7c",
"metadata": {},
"source": [
"By contrast, the string type is a composite type. Strings are reducible to smaller parts—the component characters. It might make sense to think of changing the characters in a string. But you can’t. In Python, strings are also immutable."
@@ -2160,7 +2075,6 @@
},
{
"cell_type": "markdown",
- "id": "01e81a5a",
"metadata": {},
"source": [
"The list is the first mutable data type you have encountered. Once a list has been created, elements can be added, deleted, shifted, and moved around at will. Python provides a wide range of ways to modify lists."
@@ -2168,17 +2082,18 @@
},
{
"cell_type": "markdown",
- "id": "4d6f90a7",
"metadata": {},
"source": [
- "#### Modifying a Single List Value\n",
+ "\n",
+ "#### Modifying a Single List Value [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"A single value in a list can be replaced by indexing and simple assignment:"
]
},
{
"cell_type": "code",
"execution_count": 83,
- "id": "cc6bb5c6",
"metadata": {},
"outputs": [],
"source": [
@@ -2188,7 +2103,6 @@
{
"cell_type": "code",
"execution_count": 84,
- "id": "2e5c019a",
"metadata": {},
"outputs": [
{
@@ -2209,7 +2123,6 @@
{
"cell_type": "code",
"execution_count": 85,
- "id": "82256a14",
"metadata": {},
"outputs": [],
"source": [
@@ -2219,7 +2132,6 @@
{
"cell_type": "code",
"execution_count": 86,
- "id": "3375ed03",
"metadata": {},
"outputs": [],
"source": [
@@ -2229,7 +2141,6 @@
{
"cell_type": "code",
"execution_count": 87,
- "id": "67f42e68",
"metadata": {},
"outputs": [
{
@@ -2249,7 +2160,6 @@
},
{
"cell_type": "markdown",
- "id": "72ccef34",
"metadata": {},
"source": [
"You may recall from the section on Strings and Character Data in Python that you can’t do this with a string:"
@@ -2258,7 +2168,6 @@
{
"cell_type": "code",
"execution_count": 89,
- "id": "5e0d7fcb",
"metadata": {},
"outputs": [],
"source": [
@@ -2268,7 +2177,6 @@
{
"cell_type": "code",
"execution_count": 90,
- "id": "7b2952e4",
"metadata": {},
"outputs": [
{
@@ -2289,7 +2197,6 @@
},
{
"cell_type": "markdown",
- "id": "2b911673",
"metadata": {},
"source": [
"A list item can be deleted with the `del` command:"
@@ -2298,7 +2205,6 @@
{
"cell_type": "code",
"execution_count": 92,
- "id": "18d15d1f",
"metadata": {},
"outputs": [],
"source": [
@@ -2308,7 +2214,6 @@
{
"cell_type": "code",
"execution_count": 93,
- "id": "d8caa2b0",
"metadata": {},
"outputs": [],
"source": [
@@ -2318,7 +2223,6 @@
{
"cell_type": "code",
"execution_count": 94,
- "id": "de0c5d08",
"metadata": {},
"outputs": [
{
@@ -2338,10 +2242,12 @@
},
{
"cell_type": "markdown",
- "id": "cbe55162",
"metadata": {},
"source": [
- "#### Modifying Multiple List Values\n",
+ "\n",
+ "#### Modifying Multiple List Values [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"\n",
"What if you want to change several contiguous elements in a list at one time? Python allows this with slice assignment, which has the following syntax:\n",
"\n",
@@ -2352,7 +2258,6 @@
},
{
"cell_type": "markdown",
- "id": "4a99e7fc",
"metadata": {},
"source": [
"Again, for the moment, think of an iterable as a list. This assignment replaces the specified slice of a with ``:"
@@ -2361,7 +2266,6 @@
{
"cell_type": "code",
"execution_count": 242,
- "id": "74da295b",
"metadata": {},
"outputs": [],
"source": [
@@ -2371,7 +2275,6 @@
{
"cell_type": "code",
"execution_count": 243,
- "id": "5aae64b9",
"metadata": {},
"outputs": [
{
@@ -2392,7 +2295,6 @@
{
"cell_type": "code",
"execution_count": 244,
- "id": "7ea4c9c2",
"metadata": {},
"outputs": [],
"source": [
@@ -2402,7 +2304,6 @@
{
"cell_type": "code",
"execution_count": 245,
- "id": "76994c62",
"metadata": {},
"outputs": [
{
@@ -2423,7 +2324,6 @@
{
"cell_type": "code",
"execution_count": 99,
- "id": "b56fecb6",
"metadata": {},
"outputs": [
{
@@ -2444,7 +2344,6 @@
{
"cell_type": "code",
"execution_count": 100,
- "id": "aaf1a477",
"metadata": {},
"outputs": [],
"source": [
@@ -2454,7 +2353,6 @@
{
"cell_type": "code",
"execution_count": 101,
- "id": "b23cf51b",
"metadata": {},
"outputs": [
{
@@ -2474,7 +2372,6 @@
},
{
"cell_type": "markdown",
- "id": "1cbe3508",
"metadata": {},
"source": [
"The number of elements inserted need not be equal to the number replaced. Python just grows or shrinks the list as needed."
@@ -2482,7 +2379,6 @@
},
{
"cell_type": "markdown",
- "id": "0f9fd23a",
"metadata": {},
"source": [
"You can insert multiple elements in place of a single element—just use a slice that denotes only one element:"
@@ -2491,7 +2387,6 @@
{
"cell_type": "code",
"execution_count": 102,
- "id": "246ce51d",
"metadata": {},
"outputs": [],
"source": [
@@ -2501,7 +2396,6 @@
{
"cell_type": "code",
"execution_count": 103,
- "id": "6b83bcc5",
"metadata": {},
"outputs": [],
"source": [
@@ -2511,7 +2405,6 @@
{
"cell_type": "code",
"execution_count": 104,
- "id": "f55f6bdd",
"metadata": {},
"outputs": [
{
@@ -2531,7 +2424,6 @@
},
{
"cell_type": "markdown",
- "id": "f49b0899",
"metadata": {},
"source": [
"Note that this is not the same as replacing the single element with a list:"
@@ -2540,7 +2432,6 @@
{
"cell_type": "code",
"execution_count": 106,
- "id": "949ce113",
"metadata": {},
"outputs": [],
"source": [
@@ -2550,7 +2441,6 @@
{
"cell_type": "code",
"execution_count": 107,
- "id": "11c7f9f6",
"metadata": {},
"outputs": [],
"source": [
@@ -2560,7 +2450,6 @@
{
"cell_type": "code",
"execution_count": 108,
- "id": "8d58652b",
"metadata": {},
"outputs": [
{
@@ -2580,7 +2469,6 @@
},
{
"cell_type": "markdown",
- "id": "5d73ed33",
"metadata": {},
"source": [
"You can also insert elements into a list without removing anything. Simply specify a slice of the form `[n:n]` (a zero-length slice) at the desired index:"
@@ -2589,7 +2477,6 @@
{
"cell_type": "code",
"execution_count": 109,
- "id": "1a9c2a32",
"metadata": {},
"outputs": [],
"source": [
@@ -2599,7 +2486,6 @@
{
"cell_type": "code",
"execution_count": 110,
- "id": "d0626b56",
"metadata": {},
"outputs": [],
"source": [
@@ -2609,7 +2495,6 @@
{
"cell_type": "code",
"execution_count": 111,
- "id": "4e7c3244",
"metadata": {},
"outputs": [
{
@@ -2629,7 +2514,6 @@
},
{
"cell_type": "markdown",
- "id": "8bba76d8",
"metadata": {},
"source": [
"You can delete multiple elements out of the middle of a list by assigning the appropriate slice to an empty list. You can also use the del statement with the same slice:"
@@ -2638,7 +2522,6 @@
{
"cell_type": "code",
"execution_count": 115,
- "id": "7bcf5f1f",
"metadata": {},
"outputs": [],
"source": [
@@ -2648,7 +2531,6 @@
{
"cell_type": "code",
"execution_count": 116,
- "id": "99f2580f",
"metadata": {},
"outputs": [],
"source": [
@@ -2658,7 +2540,6 @@
{
"cell_type": "code",
"execution_count": 117,
- "id": "d047bbff",
"metadata": {},
"outputs": [
{
@@ -2679,7 +2560,6 @@
{
"cell_type": "code",
"execution_count": 118,
- "id": "6c54c55b",
"metadata": {},
"outputs": [],
"source": [
@@ -2689,7 +2569,6 @@
{
"cell_type": "code",
"execution_count": 119,
- "id": "9dfeb0e2",
"metadata": {},
"outputs": [],
"source": [
@@ -2699,7 +2578,6 @@
{
"cell_type": "code",
"execution_count": 246,
- "id": "38b8462e",
"metadata": {},
"outputs": [
{
@@ -2719,10 +2597,11 @@
},
{
"cell_type": "markdown",
- "id": "b7219cc5",
"metadata": {},
"source": [
- "#### Prepending or Appending Items to a List\n",
+ "\n",
+ "#### Prepending or Appending Items to a List [
](#table_of_contents)\n",
+ "\n",
"\n",
"Additional items can be added to the start or end of a list using the + concatenation operator or the `+=` augmented assignment operator:"
]
@@ -2730,7 +2609,6 @@
{
"cell_type": "code",
"execution_count": 121,
- "id": "0d66853f",
"metadata": {},
"outputs": [],
"source": [
@@ -2740,7 +2618,6 @@
{
"cell_type": "code",
"execution_count": 122,
- "id": "43e20929",
"metadata": {},
"outputs": [],
"source": [
@@ -2750,7 +2627,6 @@
{
"cell_type": "code",
"execution_count": 123,
- "id": "15bde346",
"metadata": {},
"outputs": [
{
@@ -2771,7 +2647,6 @@
{
"cell_type": "code",
"execution_count": 124,
- "id": "3a607bd9",
"metadata": {},
"outputs": [],
"source": [
@@ -2781,7 +2656,6 @@
{
"cell_type": "code",
"execution_count": 125,
- "id": "797c39bb",
"metadata": {},
"outputs": [],
"source": [
@@ -2791,7 +2665,6 @@
{
"cell_type": "code",
"execution_count": 126,
- "id": "11c45a8b",
"metadata": {},
"outputs": [
{
@@ -2811,7 +2684,6 @@
},
{
"cell_type": "markdown",
- "id": "12bbfca7",
"metadata": {},
"source": [
"Note that a list must be concatenated with another list, so if you want to add only one element, you need to specify it as a singleton list:"
@@ -2820,7 +2692,6 @@
{
"cell_type": "code",
"execution_count": 127,
- "id": "6169fab4",
"metadata": {},
"outputs": [
{
@@ -2843,7 +2714,6 @@
{
"cell_type": "code",
"execution_count": 128,
- "id": "8168bcf0",
"metadata": {},
"outputs": [],
"source": [
@@ -2853,7 +2723,6 @@
{
"cell_type": "code",
"execution_count": 119,
- "id": "53ec6cc3",
"metadata": {},
"outputs": [
{
@@ -2873,7 +2742,6 @@
},
{
"cell_type": "markdown",
- "id": "c432ab55",
"metadata": {},
"source": [
"> **Note:** Technically, it isn’t quite correct to say a list must be concatenated with another list. More precisely, a list must be concatenated with an object that is iterable. Of course, lists are iterable, so it works to concatenate a list with another list.\n",
@@ -2897,17 +2765,18 @@
},
{
"cell_type": "markdown",
- "id": "3c679f5f",
"metadata": {},
"source": [
- "### 6. Lists Are Dynamic\n",
+ "\n",
+ "### 6. Lists Are Dynamic [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"This tutorial began with a list of six defining characteristics of Python lists. The last one is that lists are dynamic. You have seen many examples of this in the sections above. When items are added to a list, it grows as needed:"
]
},
{
"cell_type": "code",
"execution_count": 135,
- "id": "07222c9a",
"metadata": {},
"outputs": [],
"source": [
@@ -2917,7 +2786,6 @@
{
"cell_type": "code",
"execution_count": 136,
- "id": "90e7fd91",
"metadata": {},
"outputs": [],
"source": [
@@ -2927,7 +2795,6 @@
{
"cell_type": "code",
"execution_count": 137,
- "id": "c860669c",
"metadata": {},
"outputs": [],
"source": [
@@ -2937,7 +2804,6 @@
{
"cell_type": "code",
"execution_count": 138,
- "id": "59f45737",
"metadata": {},
"outputs": [
{
@@ -2957,7 +2823,6 @@
},
{
"cell_type": "markdown",
- "id": "efebeb5e",
"metadata": {},
"source": [
"Similarly, a list shrinks to accommodate the removal of items:"
@@ -2966,7 +2831,6 @@
{
"cell_type": "code",
"execution_count": 139,
- "id": "9e8025f4",
"metadata": {},
"outputs": [],
"source": [
@@ -2976,7 +2840,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "1bfa206c",
"metadata": {},
"outputs": [],
"source": [
@@ -2986,7 +2849,6 @@
{
"cell_type": "code",
"execution_count": 141,
- "id": "bcb842d3",
"metadata": {},
"outputs": [],
"source": [
@@ -2996,7 +2858,6 @@
{
"cell_type": "code",
"execution_count": 279,
- "id": "c7f29cd7",
"metadata": {
"scrolled": true
},
@@ -3018,21 +2879,23 @@
},
{
"cell_type": "markdown",
- "id": "779ebac7",
"metadata": {},
"source": [
- "\n",
- "## Python Tuples\n",
+ "\n",
+ "## Python Tuples [
](#table_of_contents)\n",
+ "\n",
"\n",
"Python provides another type that is an ordered collection of objects, called a tuple."
]
},
{
"cell_type": "markdown",
- "id": "9d19ee98",
"metadata": {},
"source": [
- "### Defining and Using Tuples\n",
+ "\n",
+ "### Defining and Using Tuples [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"Tuples are identical to lists in all respects, except for the following properties:\n",
"\n",
"- Tuples are defined by enclosing the elements in parentheses (`()`) instead of square brackets `[]`).\n",
@@ -3041,7 +2904,6 @@
},
{
"cell_type": "markdown",
- "id": "58fc91ae",
"metadata": {},
"source": [
"Here is a short example showing a tuple definition, indexing, and slicing:"
@@ -3050,7 +2912,6 @@
{
"cell_type": "code",
"execution_count": 299,
- "id": "8a21fd5f",
"metadata": {},
"outputs": [],
"source": [
@@ -3060,7 +2921,6 @@
{
"cell_type": "code",
"execution_count": 300,
- "id": "4b5c124e",
"metadata": {},
"outputs": [
{
@@ -3081,7 +2941,6 @@
{
"cell_type": "code",
"execution_count": 301,
- "id": "e7cd310e",
"metadata": {},
"outputs": [
{
@@ -3102,7 +2961,6 @@
{
"cell_type": "code",
"execution_count": 302,
- "id": "b447fd42",
"metadata": {},
"outputs": [
{
@@ -3123,7 +2981,6 @@
{
"cell_type": "code",
"execution_count": 303,
- "id": "4a97577a",
"metadata": {},
"outputs": [
{
@@ -3143,7 +3000,6 @@
},
{
"cell_type": "markdown",
- "id": "aa9a0f3b",
"metadata": {},
"source": [
"Never fear! Our favorite string and list reversal mechanism works for tuples as well:"
@@ -3152,7 +3008,6 @@
{
"cell_type": "code",
"execution_count": 304,
- "id": "c0e9af40",
"metadata": {},
"outputs": [
{
@@ -3172,7 +3027,6 @@
},
{
"cell_type": "markdown",
- "id": "fdb1c32a",
"metadata": {},
"source": [
"> Note: Even though tuples are defined using parentheses, you still index and slice tuples using square brackets, just as for strings and lists."
@@ -3180,7 +3034,6 @@
},
{
"cell_type": "markdown",
- "id": "ca8d5e75",
"metadata": {},
"source": [
"Everything you’ve learned about lists—they are ordered, they can contain arbitrary objects, they can be indexed and sliced, they can be nested—is true of tuples as well. But they can’t be modified:"
@@ -3189,7 +3042,6 @@
{
"cell_type": "code",
"execution_count": 305,
- "id": "29c14a99",
"metadata": {},
"outputs": [
{
@@ -3211,7 +3063,6 @@
},
{
"cell_type": "markdown",
- "id": "d3c309c0",
"metadata": {},
"source": [
"Why use a tuple instead of a list?\n",
@@ -3225,7 +3076,6 @@
},
{
"cell_type": "markdown",
- "id": "77c610d9",
"metadata": {},
"source": [
"In a Python REPL session, you can display the values of several objects simultaneously by entering them directly at the `>>>` prompt, separated by commas:"
@@ -3234,7 +3084,6 @@
{
"cell_type": "code",
"execution_count": 150,
- "id": "ab20cb42",
"metadata": {},
"outputs": [],
"source": [
@@ -3244,7 +3093,6 @@
{
"cell_type": "code",
"execution_count": 151,
- "id": "33a267f4",
"metadata": {},
"outputs": [],
"source": [
@@ -3254,7 +3102,6 @@
{
"cell_type": "code",
"execution_count": 152,
- "id": "0ca51e51",
"metadata": {},
"outputs": [
{
@@ -3274,7 +3121,6 @@
},
{
"cell_type": "markdown",
- "id": "dca838bf",
"metadata": {},
"source": [
"Python displays the response in parentheses because it is implicitly interpreting the input as a tuple."
@@ -3282,7 +3128,6 @@
},
{
"cell_type": "markdown",
- "id": "1a4ca1ce",
"metadata": {},
"source": [
"There is one peculiarity regarding tuple definition that you should be aware of. There is no ambiguity when defining an empty tuple, nor one with two or more elements. Python knows you are defining a tuple:"
@@ -3291,7 +3136,6 @@
{
"cell_type": "code",
"execution_count": 346,
- "id": "71c13a4e",
"metadata": {},
"outputs": [
{
@@ -3313,7 +3157,6 @@
{
"cell_type": "code",
"execution_count": 347,
- "id": "9182eb81",
"metadata": {},
"outputs": [
{
@@ -3335,7 +3178,6 @@
{
"cell_type": "code",
"execution_count": 348,
- "id": "0f792121",
"metadata": {},
"outputs": [
{
@@ -3356,7 +3198,6 @@
},
{
"cell_type": "markdown",
- "id": "94149463",
"metadata": {},
"source": [
"But what happens when you try to define a tuple with one item:"
@@ -3365,7 +3206,6 @@
{
"cell_type": "code",
"execution_count": 349,
- "id": "482ddc41",
"metadata": {},
"outputs": [
{
@@ -3386,7 +3226,6 @@
},
{
"cell_type": "markdown",
- "id": "278c54f2",
"metadata": {},
"source": [
"Doh! Since parentheses are also used to define operator precedence in expressions, Python evaluates the expression `(2)` as simply the integer `2` and creates an int object. To tell Python that you really want to define a singleton tuple, include a trailing comma `(,)` just before the closing parenthesis:"
@@ -3395,7 +3234,6 @@
{
"cell_type": "code",
"execution_count": 350,
- "id": "978af638",
"metadata": {},
"outputs": [
{
@@ -3417,7 +3255,6 @@
{
"cell_type": "code",
"execution_count": 351,
- "id": "dc15be8f",
"metadata": {},
"outputs": [
{
@@ -3438,7 +3275,6 @@
{
"cell_type": "code",
"execution_count": 352,
- "id": "221ed952",
"metadata": {},
"outputs": [
{
@@ -3458,7 +3294,6 @@
},
{
"cell_type": "markdown",
- "id": "e67604f3",
"metadata": {},
"source": [
"You probably won’t need to define a singleton tuple often, but there has to be a way."
@@ -3466,7 +3301,6 @@
},
{
"cell_type": "markdown",
- "id": "f1c8e7d6",
"metadata": {},
"source": [
"When you display a singleton tuple, Python includes the comma, to remind you that it’s a tuple:"
@@ -3475,7 +3309,6 @@
{
"cell_type": "code",
"execution_count": 166,
- "id": "fc8bc0fc",
"metadata": {},
"outputs": [
{
@@ -3495,10 +3328,11 @@
},
{
"cell_type": "markdown",
- "id": "89dd7789",
"metadata": {},
"source": [
- "### Tuple Assignment, Packing, and Unpacking\n",
+ "\n",
+ "### Tuple Assignment, Packing, and Unpacking [
](#table_of_contents)\n",
+ "\n",
"\n",
"As you have already seen above, a literal tuple containing several items can be assigned to a single object:"
]
@@ -3506,7 +3340,6 @@
{
"cell_type": "code",
"execution_count": 69,
- "id": "b4bdef2f",
"metadata": {},
"outputs": [],
"source": [
@@ -3515,7 +3348,6 @@
},
{
"cell_type": "markdown",
- "id": "303f6805",
"metadata": {},
"source": [
"
"
@@ -3524,7 +3356,6 @@
{
"cell_type": "code",
"execution_count": 70,
- "id": "9db2d631",
"metadata": {},
"outputs": [
{
@@ -3545,7 +3376,6 @@
{
"cell_type": "code",
"execution_count": 71,
- "id": "7a298e62",
"metadata": {},
"outputs": [
{
@@ -3566,7 +3396,6 @@
{
"cell_type": "code",
"execution_count": 72,
- "id": "8e9180a0",
"metadata": {},
"outputs": [
{
@@ -3586,7 +3415,6 @@
},
{
"cell_type": "markdown",
- "id": "7d2ce889",
"metadata": {},
"source": [
"If that “packed” object is subsequently assigned to a new tuple, the individual items are “unpacked” into the objects in the tuple:"
@@ -3594,7 +3422,6 @@
},
{
"cell_type": "markdown",
- "id": "2cf73f03",
"metadata": {},
"source": [
"
"
@@ -3603,7 +3430,6 @@
{
"cell_type": "code",
"execution_count": 174,
- "id": "022d4f57",
"metadata": {},
"outputs": [],
"source": [
@@ -3613,7 +3439,6 @@
{
"cell_type": "code",
"execution_count": 175,
- "id": "bf059422",
"metadata": {},
"outputs": [
{
@@ -3634,7 +3459,6 @@
{
"cell_type": "code",
"execution_count": 176,
- "id": "dd38c5cf",
"metadata": {},
"outputs": [
{
@@ -3655,7 +3479,6 @@
{
"cell_type": "code",
"execution_count": 177,
- "id": "f1b61f04",
"metadata": {},
"outputs": [
{
@@ -3676,7 +3499,6 @@
{
"cell_type": "code",
"execution_count": 178,
- "id": "89c0d428",
"metadata": {},
"outputs": [
{
@@ -3696,7 +3518,6 @@
},
{
"cell_type": "markdown",
- "id": "b145cde5",
"metadata": {},
"source": [
"When unpacking, the number of variables on the left must match the number of values in the tuple:"
@@ -3705,7 +3526,6 @@
{
"cell_type": "code",
"execution_count": 191,
- "id": "daee90ca",
"metadata": {},
"outputs": [
{
@@ -3727,7 +3547,6 @@
{
"cell_type": "code",
"execution_count": 192,
- "id": "36b6bcb2",
"metadata": {},
"outputs": [
{
@@ -3748,7 +3567,6 @@
},
{
"cell_type": "markdown",
- "id": "7048d14f",
"metadata": {},
"source": [
"Packing and unpacking can be combined into one statement to make a compound assignment:"
@@ -3757,7 +3575,6 @@
{
"cell_type": "code",
"execution_count": 197,
- "id": "ada847c3",
"metadata": {},
"outputs": [],
"source": [
@@ -3767,7 +3584,6 @@
{
"cell_type": "code",
"execution_count": 198,
- "id": "1c204276",
"metadata": {},
"outputs": [
{
@@ -3788,7 +3604,6 @@
{
"cell_type": "code",
"execution_count": 199,
- "id": "4f181eb8",
"metadata": {},
"outputs": [
{
@@ -3809,7 +3624,6 @@
{
"cell_type": "code",
"execution_count": 200,
- "id": "bb0bf08c",
"metadata": {},
"outputs": [
{
@@ -3830,7 +3644,6 @@
{
"cell_type": "code",
"execution_count": 201,
- "id": "b0de32a6",
"metadata": {},
"outputs": [
{
@@ -3850,7 +3663,6 @@
},
{
"cell_type": "markdown",
- "id": "bdc934c6",
"metadata": {},
"source": [
"Again, the number of elements in the tuple on the left of the assignment must equal the number on the right:"
@@ -3859,7 +3671,6 @@
{
"cell_type": "code",
"execution_count": 202,
- "id": "1d1e42ef",
"metadata": {},
"outputs": [
{
@@ -3880,7 +3691,6 @@
},
{
"cell_type": "markdown",
- "id": "13931cbc",
"metadata": {},
"source": [
"In assignments like this and a small handful of other situations, Python allows the parentheses that are usually used for denoting a tuple to be left out:"
@@ -3889,7 +3699,6 @@
{
"cell_type": "code",
"execution_count": 203,
- "id": "d301d74c",
"metadata": {},
"outputs": [
{
@@ -3911,7 +3720,6 @@
{
"cell_type": "code",
"execution_count": 204,
- "id": "4eaf55b5",
"metadata": {},
"outputs": [
{
@@ -3933,7 +3741,6 @@
{
"cell_type": "code",
"execution_count": 205,
- "id": "4a2c32c5",
"metadata": {},
"outputs": [
{
@@ -3955,7 +3762,6 @@
{
"cell_type": "code",
"execution_count": 206,
- "id": "ecdd33ca",
"metadata": {},
"outputs": [
{
@@ -3976,7 +3782,6 @@
},
{
"cell_type": "markdown",
- "id": "0b5e39bb",
"metadata": {},
"source": [
"It works the same whether the parentheses are included or not, so if you have any doubt as to whether they’re needed, go ahead and include them."
@@ -3984,7 +3789,6 @@
},
{
"cell_type": "markdown",
- "id": "27814877",
"metadata": {},
"source": [
"Tuple assignment allows for a curious bit of idiomatic Python. Frequently when programming, you have two variables whose values you need to swap. In most programming languages, it is necessary to store one of the values in a temporary variable while the swap occurs like this:"
@@ -3993,7 +3797,6 @@
{
"cell_type": "code",
"execution_count": 207,
- "id": "840beb6d",
"metadata": {},
"outputs": [
{
@@ -4016,7 +3819,6 @@
{
"cell_type": "code",
"execution_count": 210,
- "id": "94eda2d2",
"metadata": {},
"outputs": [
{
@@ -4039,7 +3841,6 @@
},
{
"cell_type": "markdown",
- "id": "717db98e",
"metadata": {},
"source": [
"In Python, the swap can be done with a single tuple assignment:"
@@ -4048,7 +3849,6 @@
{
"cell_type": "code",
"execution_count": 212,
- "id": "46350be1",
"metadata": {},
"outputs": [
{
@@ -4071,7 +3871,6 @@
{
"cell_type": "code",
"execution_count": 213,
- "id": "d934380b",
"metadata": {},
"outputs": [],
"source": [
@@ -4081,7 +3880,6 @@
{
"cell_type": "code",
"execution_count": 214,
- "id": "6f2c968e",
"metadata": {},
"outputs": [
{
@@ -4101,7 +3899,6 @@
},
{
"cell_type": "markdown",
- "id": "ad4cb308",
"metadata": {},
"source": [
"As anyone who has ever had to swap values using a temporary variable knows, being able to do it this way in Python is the pinnacle of modern technological achievement. It will never get better than this."
@@ -4110,7 +3907,6 @@
{
"cell_type": "code",
"execution_count": 383,
- "id": "16b352b7",
"metadata": {},
"outputs": [],
"source": [
@@ -4120,7 +3916,6 @@
{
"cell_type": "code",
"execution_count": 384,
- "id": "41ad8b4d",
"metadata": {},
"outputs": [
{
@@ -4140,16 +3935,17 @@
},
{
"cell_type": "markdown",
- "id": "8756eecb",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"This tutorial covered the basic properties of Python **lists** and **tuples**, and how to manipulate them. You will use these extensively in your Python programming."
]
},
{
"cell_type": "markdown",
- "id": "dcc73545",
"metadata": {},
"source": [
"One of the chief characteristics of a list is that it is ordered. The order of the elements in a list is an intrinsic property of that list and does not change, unless the list itself is modified. (The same is true of tuples, except of course they can’t be modified.)"
@@ -4157,7 +3953,6 @@
},
{
"cell_type": "markdown",
- "id": "d19954dc",
"metadata": {},
"source": [
"The next tutorial will introduce you to the Python **dictionary**: a composite data type that is unordered. Read on!"
@@ -4180,7 +3975,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/09 Composite Data Types: Dictionary.ipynb b/Python/01. Basics/09 Composite Data Types: Dictionary.ipynb
similarity index 88%
rename from python/01. Basics/09 Composite Data Types: Dictionary.ipynb
rename to Python/01. Basics/09 Composite Data Types: Dictionary.ipynb
index 26116da..d69089c 100755
--- a/python/01. Basics/09 Composite Data Types: Dictionary.ipynb
+++ b/Python/01. Basics/09 Composite Data Types: Dictionary.ipynb
@@ -4,11 +4,37 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Dictionary\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Dictionary \n",
"\n",
"Python provides another composite data type called a dictionary, which is similar to a list in that it is a collection of objects."
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Defining a Dictionary](#defining_a_dictionary)\n",
+ "* [Accessing Dictionary Values](#accessing_dictionary_values)\n",
+ "* [Dictionary Keys vs. List Indices](#dictionary_keys_vs._list_indices)\n",
+ "* [Building a Dictionary Incrementally](#building_a_dictionary_incrementally)\n",
+ "* [Restrictions on Dictionary Keys](#restrictions_on_dictionary_keys)\n",
+ "* [Restrictions on Dictionary Values](#restrictions_on_dictionary_values)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -34,7 +60,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Defining a Dictionary\n",
+ "\n",
+ "## Defining a Dictionary [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"Dictionaries are Python’s implementation of a data structure that is more generally known as an associative array. A dictionary consists of a collection of key-value pairs. Each key-value pair maps the key to its associated value."
]
},
@@ -207,18 +236,18 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 18,
"metadata": {},
"outputs": [
{
- "ename": "KeyError",
- "evalue": "1",
+ "ename": "NameError",
+ "evalue": "name 'MLB_team' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mMLB_team\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mKeyError\u001b[0m: 1"
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_19329/1469821463.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mMLB_team\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m: name 'MLB_team' is not defined"
]
}
],
@@ -230,7 +259,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Accessing Dictionary Values\n",
+ "\n",
+ "## Accessing Dictionary Values [
](#table_of_contents)\n",
+ "\n",
"\n",
"Of course, dictionary elements must be accessible somehow. If you don’t get them by index, then how do you get them?"
]
@@ -385,21 +416,19 @@
},
{
"cell_type": "code",
- "execution_count": 65,
+ "execution_count": 26,
"metadata": {},
"outputs": [
{
- "data": {
- "text/plain": [
- "{'Colorado': 'Rockies',\n",
- " 'Boston': 'Red Sox',\n",
- " 'Minnesota': 'Twins',\n",
- " 'Milwaukee': 'Brewers'}"
- ]
- },
- "execution_count": 65,
- "metadata": {},
- "output_type": "execute_result"
+ "ename": "NameError",
+ "evalue": "name 'MLB_team' is not defined",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_19329/3737734966.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mdel\u001b[0m \u001b[0mMLB_team\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'Seattle'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mMLB_team\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mNameError\u001b[0m: name 'MLB_team' is not defined"
+ ]
}
],
"source": [
@@ -411,7 +440,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Dictionary Keys vs. List Indices\n",
+ "\n",
+ "## Dictionary Keys vs. List Indices [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"You may have noticed that the interpreter raises the same exception, KeyError, when a dictionary is accessed with either an undefined key or by a numeric index:"
]
},
@@ -650,7 +682,7 @@
},
{
"cell_type": "code",
- "execution_count": 83,
+ "execution_count": 50,
"metadata": {},
"outputs": [
{
@@ -660,7 +692,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0md\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_19329/3015485889.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0md\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mNameError\u001b[0m: name 'd' is not defined"
]
}
@@ -682,7 +714,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Building a Dictionary Incrementally\n",
+ "\n",
+ "## Building a Dictionary Incrementally [
](#table_of_contents)\n",
+ "\n",
"\n",
"Defining a dictionary using curly braces and a list of key-value pairs, as shown above, is fine if you know all the keys and values in advance. But what if you want to build a dictionary on the fly?"
]
@@ -696,7 +730,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 118,
"metadata": {},
"outputs": [
{
@@ -705,7 +739,7 @@
"dict"
]
},
- "execution_count": 26,
+ "execution_count": 118,
"metadata": {},
"output_type": "execute_result"
}
@@ -988,7 +1022,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Restrictions on Dictionary Keys\n",
+ "\n",
+ "## Restrictions on Dictionary Keys [
](#table_of_contents)\n",
+ "\n",
"\n",
"Almost any type of value can be used as a dictionary key in Python. You just saw this example, where integer, float, and Boolean objects are used as keys:"
]
@@ -1064,7 +1100,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 139,
"metadata": {},
"outputs": [
{
@@ -1073,7 +1109,7 @@
"3"
]
},
- "execution_count": 41,
+ "execution_count": 139,
"metadata": {},
"output_type": "execute_result"
}
@@ -1310,7 +1346,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Restrictions on Dictionary Values\n",
+ "\n",
+ "## Restrictions on Dictionary Values [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"By contrast, there are no restrictions on dictionary values. Literally none at all. A dictionary value can be any type of object Python supports, including mutable types like lists and dictionaries, and user-defined objects, which you will learn about in upcoming tutorials."
]
},
@@ -1366,7 +1405,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"In this tutorial, you covered the basic properties of the Python **dictionary** and learned how to access and manipulate dictionary data."
]
},
@@ -1408,7 +1450,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/10 Composite Data Types: Sets.ipynb b/Python/01. Basics/10 Composite Data Types: Sets.ipynb
similarity index 80%
rename from python/01. Basics/10 Composite Data Types: Sets.ipynb
rename to Python/01. Basics/10 Composite Data Types: Sets.ipynb
index b5698b1..7ae1c11 100755
--- a/python/01. Basics/10 Composite Data Types: Sets.ipynb
+++ b/Python/01. Basics/10 Composite Data Types: Sets.ipynb
@@ -4,11 +4,33 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Set\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Set \n",
"\n",
"In mathematics, a rigorous definition of a set can be abstract and difficult to grasp. Practically though, a set can be thought of simply as a well-defined collection of distinct objects, typically called **elements** or **members**."
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Defining a Set](#defining_a_set)\n",
+ "* [Set Size and Membership](#set_size_and_membership)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -20,7 +42,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Defining a Set\n",
+ "\n",
+ "## Defining a Set [
](#table_of_contents)\n",
+ "\n",
"\n",
"Python’s built-in set type has the following characteristics:\n",
"\n",
@@ -42,6 +66,142 @@
"```"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = {3, 8, 1}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "8\n",
+ "1\n",
+ "3\n"
+ ]
+ }
+ ],
+ "source": [
+ "for item in x:\n",
+ " print(item)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = {}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "a\n",
+ "e\n",
+ "h\n",
+ "l\n",
+ "s\n"
+ ]
+ }
+ ],
+ "source": [
+ "for char in 'abcdefghijklmnopqrstuvwxyz':\n",
+ " hash_value = hash(char)\n",
+ " hash_hash_value = hash(hash_value)\n",
+ " \n",
+ " if hash_value == hash_hash_value:\n",
+ " print(char)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1753726360175525413"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "hash('a')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = {\n",
+ " 'a': 1,\n",
+ " 1753726360175525413: 2,\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'a': 1, 1753726360175525413: 2}"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "5603912153990001835"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "hash('a str')"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -543,14 +703,16 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Set Size and Membership\n",
+ "\n",
+ "## Set Size and Membership [
](#table_of_contents)\n",
+ "\n",
"\n",
"The `len()` function returns the number of elements in a set, and the `in` and `not in` operators can be used to test for membership:"
]
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 107,
"metadata": {},
"outputs": [],
"source": [
@@ -559,7 +721,7 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 108,
"metadata": {},
"outputs": [
{
@@ -568,7 +730,7 @@
"3"
]
},
- "execution_count": 25,
+ "execution_count": 108,
"metadata": {},
"output_type": "execute_result"
}
@@ -599,7 +761,7 @@
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 243,
"metadata": {},
"outputs": [
{
@@ -608,7 +770,7 @@
"False"
]
},
- "execution_count": 28,
+ "execution_count": 243,
"metadata": {},
"output_type": "execute_result"
}
@@ -621,7 +783,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
"\n",
"In this tutorial, you learned how to define set objects in Python, and you became familiar with the functions, operators, and methods that can be used to work with sets."
]
@@ -657,7 +821,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/11 Python Program Lexical Structure.ipynb b/Python/01. Basics/11 Python Program Lexical Structure.ipynb
similarity index 89%
rename from python/01. Basics/11 Python Program Lexical Structure.ipynb
rename to Python/01. Basics/11 Python Program Lexical Structure.ipynb
index 1d7fe0c..1a0157a 100755
--- a/python/01. Basics/11 Python Program Lexical Structure.ipynb
+++ b/Python/01. Basics/11 Python Program Lexical Structure.ipynb
@@ -4,7 +4,14 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Python Program Lexical Structure\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Python Program Lexical Structure \n",
"\n",
"You have now covered Python variables, operators, and data types in depth, and you’ve seen quite a bit of example code. Up to now, the code has consisted of short individual statements, simply assigning objects to variables or displaying values.\n",
"\n",
@@ -15,7 +22,33 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Python Statements\n",
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [Python Statements](#python_statements)\n",
+ "* [Line Continuation](#line_continuation)\n",
+ " * [Implicit Line Continuation](#implicit_line_continuation)\n",
+ " * [Parentheses](#parentheses)\n",
+ " * [Curly Braces](#curly_braces)\n",
+ " * [Square Brackets](#square_brackets)\n",
+ " * [Explicit Line Continuation](#explicit_line_continuation)\n",
+ "* [Multiple Statements Per Line](#multiple_statements_per_line)\n",
+ "* [Comments](#comments)\n",
+ "* [Whitespace](#whitespace)\n",
+ "* [Whitespace as Indentation](#whitespace_as_indentation)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Python Statements [
](#table_of_contents)\n",
+ "\n",
"\n",
"Statements are the basic units of instruction that the Python interpreter parses and processes. In general, the interpreter executes statements sequentially, one after the next as it encounters them. (You will see in the next tutorial on conditional statements that it is possible to alter this behavior.)"
]
@@ -73,14 +106,16 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Line Continuation\n",
+ "\n",
+ "## Line Continuation [
](#table_of_contents)\n",
+ "\n",
"\n",
"Suppose a single statement in your Python code is especially long. For example, you may have an assignment statement with many terms:"
]
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
@@ -91,7 +126,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 12,
"metadata": {},
"outputs": [
{
@@ -100,7 +135,7 @@
"True"
]
},
- "execution_count": 7,
+ "execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@@ -186,15 +221,15 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 15,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
- "evalue": "invalid syntax (, line 1)",
+ "evalue": "invalid syntax (1911760183.py, line 1)",
"output_type": "error",
"traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m someone_is_of_working_age = person1_age >= 18 and person1_age <= 65 or\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
+ "\u001b[0;36m File \u001b[0;32m\"/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_50053/1911760183.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m someone_is_of_working_age = person1_age >= 18 and person1_age <= 65 or\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
]
}
],
@@ -213,7 +248,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Implicit Line Continuation\n",
+ "\n",
+ "### Implicit Line Continuation [
](#table_of_contents)\n",
+ "\n",
"\n",
"This is the more straightforward technique for line continuation, and the one that is preferred according to PEP 8."
]
@@ -234,7 +271,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
@@ -249,7 +286,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 26,
"metadata": {},
"outputs": [
{
@@ -262,7 +299,7 @@
" [21, 22, 23, 24, 25]]"
]
},
- "execution_count": 12,
+ "execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
@@ -322,7 +359,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Parentheses\n",
+ "\n",
+ "#### Parentheses [
](#table_of_contents)\n",
+ "\n",
"\n",
"- Expression grouping"
]
@@ -369,7 +408,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 28,
"metadata": {},
"outputs": [
{
@@ -441,7 +480,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Curly Braces"
+ "\n",
+ "#### Curly Braces [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -487,7 +528,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Square Brackets"
+ "\n",
+ "#### Square Brackets [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -612,7 +655,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 35,
"metadata": {},
"outputs": [],
"source": [
@@ -631,7 +674,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 36,
"metadata": {},
"outputs": [
{
@@ -640,7 +683,7 @@
"[[['foo', 'bar'], [1, 2, 3]], {1, 3, 5}, {'a': 1, 'b': 2}]"
]
},
- "execution_count": 27,
+ "execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
@@ -660,7 +703,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Explicit Line Continuation"
+ "\n",
+ "### Explicit Line Continuation [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -722,7 +767,7 @@
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -752,7 +797,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 44,
"metadata": {},
"outputs": [],
"source": [
@@ -763,7 +808,7 @@
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 45,
"metadata": {},
"outputs": [
{
@@ -772,7 +817,7 @@
"21"
]
},
- "execution_count": 35,
+ "execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
@@ -818,7 +863,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Multiple Statements Per Line\n",
+ "\n",
+ "## Multiple Statements Per Line [
](#table_of_contents)\n",
+ "\n",
"\n",
"Multiple statements may occur on one line, if they are separated by a semicolon (`;`) character:"
]
@@ -860,7 +907,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 57,
"metadata": {},
"outputs": [],
"source": [
@@ -869,7 +916,7 @@
},
{
"cell_type": "code",
- "execution_count": 42,
+ "execution_count": 58,
"metadata": {},
"outputs": [
{
@@ -904,7 +951,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Comments\n",
+ "\n",
+ "## Comments [
](#table_of_contents)\n",
+ "\n",
"\n",
"In Python, the hash character (`#`) signifies a comment. The interpreter will ignore everything from the hash character through the end of that line:"
]
@@ -964,7 +1013,7 @@
},
{
"cell_type": "code",
- "execution_count": 54,
+ "execution_count": 59,
"metadata": {},
"outputs": [
{
@@ -973,7 +1022,7 @@
"'foobar # I am *not* a comment.'"
]
},
- "execution_count": 54,
+ "execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
@@ -997,7 +1046,6 @@
"outputs": [],
"source": [
"# Calculate and display the area of a circle.\n",
- "\n",
"pi = 3.1415926536\n",
"r = 12.35"
]
@@ -1212,7 +1260,7 @@
},
{
"cell_type": "code",
- "execution_count": 64,
+ "execution_count": 5,
"metadata": {},
"outputs": [
{
@@ -1261,7 +1309,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Although this works (and was once put forth as a Python programming tip by Guido himself), PEP 8 actually recommends against it. The reason for this appears to be because of a special Python construct called the **docstring***. A docstring is a special comment at the beginning of a user-defined function that documents the function’s behavior. Docstrings are typically specified as triple-quoted string comments, so PEP 8 recommends that other [block comments](https://www.python.org/dev/peps/pep-0008/?#block-comments) in Python code be designated the usual way, with a hash character at the start of each line."
+ "Although this works (and was once put forth as a Python programming tip by Guido himself), PEP 8 actually recommends against it. The reason for this appears to be because of a special Python construct called the **docstring**. A docstring is a special comment at the beginning of a user-defined function that documents the function’s behavior. Docstrings are typically specified as triple-quoted string comments, so PEP 8 recommends that other [block comments](https://www.python.org/dev/peps/pep-0008/?#block-comments) in Python code be designated the usual way, with a hash character at the start of each line."
]
},
{
@@ -1282,7 +1330,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Whitespace\n",
+ "\n",
+ "## Whitespace [
](#table_of_contents)\n",
+ "\n",
"\n",
"When parsing code, the Python interpreter breaks the input up into tokens. Informally, tokens are just the language elements that you have seen so far: identifiers, keywords, literals, and operators."
]
@@ -1307,6 +1357,16 @@
"There are other somewhat outdated ASCII whitespace characters such as line feed and form feed, as well as some very esoteric Unicode characters that provide whitespace. But for present purposes, whitespace usually means a space, tab, or newline."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = 3\n",
+ "x=2"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -1475,6 +1535,15 @@
"Most people would likely find that the added whitespace in the second example makes it easier to read. On the other hand, you could probably find a few who would prefer the first example. To some extent, it is a matter of personal preference. But there are standards for [whitespace in expressions and statements](https://www.python.org/dev/peps/pep-0008/?#whitespace-in-expressions-and-statements) put forth in PEP 8, and you should strongly consider adhering to them as much as possible."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = (1, )"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -1715,7 +1784,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Whitespace as Indentation\n",
+ "\n",
+ "## Whitespace as Indentation [
](#table_of_contents)\n",
+ "\n",
"\n",
"There is one more important situation in which whitespace is significant in Python code. Indentation—whitespace that appears to the left of the first token on a line—has very special meaning."
]
@@ -1795,7 +1866,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
"\n",
"This tutorial introduced you to Python program lexical structure. You learned what constitutes a valid Python **statement** and how to use **implicit** and **explicit line continuation** to write a statement that spans multiple lines. You also learned about commenting Python code, and about use of whitespace to enhance readability."
]
@@ -1824,7 +1897,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/12 Conditional Statements in Python.ipynb b/Python/01. Basics/12 Conditional Statements in Python.ipynb
similarity index 89%
rename from python/01. Basics/12 Conditional Statements in Python.ipynb
rename to Python/01. Basics/12 Conditional Statements in Python.ipynb
index 47ada3f..d99b680 100755
--- a/python/01. Basics/12 Conditional Statements in Python.ipynb
+++ b/Python/01. Basics/12 Conditional Statements in Python.ipynb
@@ -4,7 +4,36 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Conditional Statements in Python"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Conditional Statements in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Introduction to the `if` Statement](#introduction_to_the_`if`_statement)\n",
+ "* [Grouping Statements: Indentation and Blocks](#grouping_statements:_indentation_and_blocks)\n",
+ " * [Python: It’s All About the Indentation](#python:_it’s_all_about_the_indentation)\n",
+ " * [What Do Other Languages Do?](#what_do_other_languages_do?)\n",
+ " * [Which Is Better?](#which_is_better?)\n",
+ "* [The `else` and `elif` Clauses](#the_`else`_and_`elif`_clauses)\n",
+ "* [One-Line `if` Statements](#one-line_`if`_statements)\n",
+ "* [Conditional Expressions (Python’s Ternary Operator)](#conditional_expressions_(python’s_ternary_operator))\n",
+ "* [The Python `pass` Statement](#the_python_`pass`_statement)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
@@ -59,7 +88,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Introduction to the `if` Statement"
+ "\n",
+ "## Introduction to the `if` Statement [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -237,7 +268,7 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
@@ -249,7 +280,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Grouping Statements: Indentation and Blocks"
+ "\n",
+ "## Grouping Statements: Indentation and Blocks [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -294,7 +327,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Python: It’s All About the Indentation"
+ "\n",
+ "### Python: It’s All About the Indentation [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -403,7 +438,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 29,
"metadata": {},
"outputs": [
{
@@ -440,7 +475,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### What Do Other Languages Do?"
+ "\n",
+ "### What Do Other Languages Do? [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -488,7 +525,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Which Is Better?"
+ "\n",
+ "### Which Is Better? [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -537,7 +576,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The `else` and `elif` Clauses"
+ "\n",
+ "## The `else` and `elif` Clauses [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -732,7 +773,7 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": 41,
"metadata": {},
"outputs": [
{
@@ -742,7 +783,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mvar\u001b[0m \u001b[0;31m# Not defined\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_10923/504837137.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mvar\u001b[0m \u001b[0;31m# Not defined\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mNameError\u001b[0m: name 'var' is not defined"
]
}
@@ -784,7 +825,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## One-Line `if` Statements"
+ "\n",
+ "## One-Line `if` Statements [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -883,7 +926,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 45,
"metadata": {},
"outputs": [
{
@@ -939,7 +982,7 @@
},
{
"cell_type": "code",
- "execution_count": 62,
+ "execution_count": 46,
"metadata": {},
"outputs": [
{
@@ -983,11 +1026,33 @@
"```"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "False\n"
+ ]
+ }
+ ],
+ "source": [
+ "if x < 5:\n",
+ " print('True')\n",
+ "else:\n",
+ " print('False')"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conditional Expressions (Python’s Ternary Operator)"
+ "\n",
+ "## Conditional Expressions (Python’s Ternary Operator) [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1024,7 +1089,7 @@
},
{
"cell_type": "code",
- "execution_count": 71,
+ "execution_count": 78,
"metadata": {},
"outputs": [
{
@@ -1042,7 +1107,7 @@
},
{
"cell_type": "code",
- "execution_count": 72,
+ "execution_count": 79,
"metadata": {},
"outputs": [
{
@@ -1263,14 +1328,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- },
- {
- "cell_type": "code",
- "execution_count": 35,
+ "execution_count": 80,
"metadata": {},
"outputs": [
{
@@ -1279,7 +1337,7 @@
"'foo'"
]
},
- "execution_count": 35,
+ "execution_count": 80,
"metadata": {},
"output_type": "execute_result"
}
@@ -1290,7 +1348,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 81,
"metadata": {},
"outputs": [
{
@@ -1299,7 +1357,7 @@
"'bar'"
]
},
- "execution_count": 36,
+ "execution_count": 81,
"metadata": {},
"output_type": "execute_result"
}
@@ -1367,7 +1425,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The Python `pass` Statement"
+ "\n",
+ "## The Python `pass` Statement [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1377,6 +1437,32 @@
"Occasionally, you may find that you want to write what is called a code stub: a placeholder for where you will eventually put a block of code that you haven’t implemented yet."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 91,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "here\n"
+ ]
+ }
+ ],
+ "source": [
+ "name = 'Ali'\n",
+ "\n",
+ "if name == 'Ali':\n",
+ " # TODO: add name to database\n",
+ " pass\n",
+ "else:\n",
+ " # TODO: warn user about registeration\n",
+ " pass\n",
+ "\n",
+ "print('here')"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -1456,7 +1542,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion"
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1501,7 +1589,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git "a/python/01. Basics/13 Python \"while\" Loops (Indefinite Iteration).ipynb" "b/Python/01. Basics/13 Python \"while\" Loops (Indefinite Iteration).ipynb"
similarity index 87%
rename from "python/01. Basics/13 Python \"while\" Loops (Indefinite Iteration).ipynb"
rename to "Python/01. Basics/13 Python \"while\" Loops (Indefinite Iteration).ipynb"
index 8c1a068..26f97ce 100755
--- "a/python/01. Basics/13 Python \"while\" Loops (Indefinite Iteration).ipynb"
+++ "b/Python/01. Basics/13 Python \"while\" Loops (Indefinite Iteration).ipynb"
@@ -4,7 +4,33 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Python \"while\" Loops (Indefinite Iteration)"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Python \"while\" Loops (Indefinite Iteration) \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [The `while` Loop](#the_`while`_loop)\n",
+ "* [The Python `break` and `continue` Statements](#the_python_`break`_and_`continue`_statements)\n",
+ "* [The `else` Clause](#the_`else`_clause)\n",
+ "* [Infinite Loops](#infinite_loops)\n",
+ "* [Nested `while` Loops](#nested_`while`_loops)\n",
+ "* [One-Line `while` Loops](#one-line_`while`_loops)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
@@ -47,7 +73,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The `while` Loop"
+ "\n",
+ "## The `while` Loop [
](#table_of_contents)\n"
]
},
{
@@ -69,13 +96,6 @@
"```"
]
},
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "`` represents the block to be repeatedly executed, often referred to as the body of the loop. This is denoted with indentation, just as in an if statement."
- ]
- },
{
"cell_type": "markdown",
"metadata": {},
@@ -106,7 +126,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
@@ -202,11 +222,58 @@
"When a list is evaluated in Boolean context, it is truthy if it has elements in it and falsy if it is empty. In this example, a is true as long as it has elements in it. Once all the items have been removed with the `.pop()` method and the list is empty, `a` is false, and the loop terminates."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 44,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "here\n"
+ ]
+ }
+ ],
+ "source": [
+ "x = 5\n",
+ "if x < 5:\n",
+ " print('here')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = 5\n",
+ "while x < 5:\n",
+ " print('here')"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The Python `break` and `continue` Statements"
+ "\n",
+ "## The Python `break` and `continue` Statements [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = 5\n",
+ "\n",
+ "if x == 5:\n",
+ " print('x is 5')\n",
+ "elif x > 6:\n",
+ " print('x is greater than 6')\n",
+ "else:\n",
+ " print('else')"
]
},
{
@@ -313,7 +380,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The `else` Clause"
+ "\n",
+ "## The `else` Clause [
](#table_of_contents)\n"
]
},
{
@@ -472,6 +540,46 @@
"When might an `else` clause on a while loop be useful? One common situation is if you are searching a list for a specific item. You can use `break` to exit the loop if the item is found, and the `else` clause can contain code that is meant to be executed if the item isn’t found:"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "5\n",
+ "4\n",
+ "3\n"
+ ]
+ }
+ ],
+ "source": [
+ "n = 5\n",
+ "while n > 0:\n",
+ " print(n)\n",
+ " \n",
+ " if n % 3 == 0:\n",
+ " break\n",
+ " n -= 1\n",
+ "else:\n",
+ " print('found')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "while n > 0:\n",
+ " print(n)\n",
+ " n -= 1\n",
+ "\n",
+ "print('loop finished')"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 26,
@@ -542,7 +650,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Infinite Loops"
+ "\n",
+ "## Infinite Loops [
](#table_of_contents)\n"
]
},
{
@@ -640,7 +749,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Nested `while` Loops"
+ "\n",
+ "## Nested `while` Loops [
](#table_of_contents)\n"
]
},
{
@@ -790,7 +900,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## One-Line `while` Loops"
+ "\n",
+ "## One-Line `while` Loops [
](#table_of_contents)\n"
]
},
{
@@ -882,7 +993,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion"
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n"
]
},
{
@@ -928,7 +1040,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git "a/python/01. Basics/14 Python \"for\" Loops (Definite Iteration).ipynb" "b/Python/01. Basics/14 Python \"for\" Loops (Definite Iteration).ipynb"
similarity index 88%
rename from "python/01. Basics/14 Python \"for\" Loops (Definite Iteration).ipynb"
rename to "Python/01. Basics/14 Python \"for\" Loops (Definite Iteration).ipynb"
index 2f0d0b5..5c8764d 100755
--- "a/python/01. Basics/14 Python \"for\" Loops (Definite Iteration).ipynb"
+++ "b/Python/01. Basics/14 Python \"for\" Loops (Definite Iteration).ipynb"
@@ -4,7 +4,40 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Python \"for\" Loops (Definite Iteration)"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Python \"for\" Loops (Definite Iteration) \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [A Survey of Definite Iteration in Programming](#a_survey_of_definite_iteration_in_programming)\n",
+ " * [Numeric Range Loop](#numeric_range_loop)\n",
+ " * [Three-Expression Loop](#three-expression_loop)\n",
+ " * [Collection-Based or Iterator-Based Loop](#collection-based_or_iterator-based_loop)\n",
+ " * [The Python for Loop](#the_python_for_loop)\n",
+ " * [Iterables](#iterables)\n",
+ " * [Iterators](#iterators)\n",
+ "* [The Guts of the Python for Loop](#the_guts_of_the_python_for_loop)\n",
+ "* [Iterating Through a Dictionary](#iterating_through_a_dictionary)\n",
+ "* [The `range()` Function](#the_`range()`_function)\n",
+ "* [Altering `for` Loop Behavior](#altering_`for`_loop_behavior)\n",
+ " * [The break and continue Statements](#the_break_and_continue_statements)\n",
+ " * [The `else` Clause](#the_`else`_clause)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
@@ -57,7 +90,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## A Survey of Definite Iteration in Programming"
+ "\n",
+ "## A Survey of Definite Iteration in Programming [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -73,8 +108,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "\n",
- "### Numeric Range Loop"
+ "\n",
+ "### Numeric Range Loop [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -100,7 +136,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Three-Expression Loop"
+ "\n",
+ "### Three-Expression Loop [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -149,7 +187,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Collection-Based or Iterator-Based Loop"
+ "\n",
+ "### Collection-Based or Iterator-Based Loop [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -182,7 +222,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### The Python for Loop"
+ "\n",
+ "### The Python for Loop [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -263,7 +305,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Iterables\n",
+ "\n",
+ "### Iterables [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"In Python, **iterable** means an object can be used in iteration. The term is used as:\n",
"\n",
"- **An adjective:** An object may be described as iterable.\n",
@@ -393,7 +438,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 49,
"metadata": {},
"outputs": [
{
@@ -403,7 +448,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0miter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Integer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_76590/2887938158.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0miter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Integer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: 'int' object is not iterable"
]
}
@@ -479,7 +524,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Iterators"
+ "\n",
+ "### Iterators [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -804,7 +851,7 @@
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 51,
"metadata": {},
"outputs": [],
"source": [
@@ -813,7 +860,7 @@
},
{
"cell_type": "code",
- "execution_count": 31,
+ "execution_count": 52,
"metadata": {},
"outputs": [
{
@@ -822,7 +869,7 @@
"('foo', 'bar', 'baz')"
]
},
- "execution_count": 31,
+ "execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
@@ -834,7 +881,7 @@
},
{
"cell_type": "code",
- "execution_count": 114,
+ "execution_count": 53,
"metadata": {},
"outputs": [
{
@@ -843,7 +890,7 @@
"{'bar', 'baz', 'foo'}"
]
},
- "execution_count": 114,
+ "execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
@@ -878,7 +925,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The Guts of the Python for Loop"
+ "\n",
+ "## The Guts of the Python for Loop [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -904,7 +953,7 @@
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 64,
"metadata": {},
"outputs": [
{
@@ -973,7 +1022,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Iterating Through a Dictionary"
+ "\n",
+ "## Iterating Through a Dictionary [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -985,7 +1036,20 @@
},
{
"cell_type": "code",
- "execution_count": 34,
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "d = {\n",
+ " 'one': 1,\n",
+ " 'two': 2,\n",
+ " 'three': 3,\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 125,
"metadata": {},
"outputs": [
{
@@ -1020,7 +1084,7 @@
},
{
"cell_type": "code",
- "execution_count": 35,
+ "execution_count": 126,
"metadata": {},
"outputs": [
{
@@ -1047,7 +1111,7 @@
},
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 127,
"metadata": {},
"outputs": [
{
@@ -1149,7 +1213,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The `range()` Function"
+ "\n",
+ "## The `range()` Function [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1485,7 +1551,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Altering `for` Loop Behavior"
+ "\n",
+ "## Altering `for` Loop Behavior [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1499,7 +1567,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### The break and continue Statements"
+ "\n",
+ "### The break and continue Statements [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1561,7 +1631,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### The `else` Clause"
+ "\n",
+ "### The `else` Clause [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1628,7 +1700,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"This section presented the `for` loop, the workhorse of **definite iteration** in Python."
]
},
@@ -1663,7 +1738,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/Python/01. Basics/15 List Comprehension.ipynb b/Python/01. Basics/15 List Comprehension.ipynb
new file mode 100644
index 0000000..a0fb7ae
--- /dev/null
+++ b/Python/01. Basics/15 List Comprehension.ipynb
@@ -0,0 +1,925 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
When to Use a List Comprehension in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [How to Create Lists in Python](#how_to_create_lists_in_python)\n",
+ " * [Using for Loops](#using_for_loops)\n",
+ " * [Using `map()` Objects](#using_`map()`_objects)\n",
+ " * [Using List Comprehensions](#using_list_comprehensions)\n",
+ "* [Benefits of Using List Comprehensions](#benefits_of_using_list_comprehensions)\n",
+ "* [How to Supercharge Your Comprehensions](#how_to_supercharge_your_comprehensions)\n",
+ " * [Using Conditional Logic](#using_conditional_logic)\n",
+ " * [Using Set and Dictionary Comprehensions](#using_set_and_dictionary_comprehensions)\n",
+ "* [When Not to Use a List Comprehension in Python](#when_not_to_use_a_list_comprehension_in_python)\n",
+ " * [Watch Out for Nested Comprehensions](#watch_out_for_nested_comprehensions)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python is famous for allowing you to write code that’s elegant, easy to write, and almost as easy to read as plain English. One of the language’s most distinctive features is the list comprehension, which you can use to create powerful functionality within a single line of code. However, many developers struggle to fully leverage the more advanced features of a list comprehension in Python. Some programmers even use them too much, which can lead to code that’s less efficient and harder to read."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## How to Create Lists in Python [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are a few different ways you can create lists in Python. To better understand the trade-offs of using a list comprehension in Python, let’s first see how to create lists with these approaches."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Using for Loops [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The most common type of loop is the for loop. You can use a for loop to create a list of elements in three steps:\n",
+ "\n",
+ "- Instantiate an empty list.\n",
+ "- Loop over an iterable or range of elements.\n",
+ "- Append each element to the end of the list."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you want to create a list containing the first ten perfect squares, then you can complete these steps in three lines of code:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "squares = []\n",
+ "for i in range(10):\n",
+ " squares.append(i * i)\n",
+ "squares"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, you instantiate an empty list, squares. Then, you use a for loop to iterate over `range(10)`. Finally, you multiply each number by itself and append the result to the end of the list."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Using `map()` Objects [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`map()` provides an alternative approach that’s based in functional programming. You pass in a function and an iterable, and `map()` will create an object. This object contains the output you would get from running each iterable element through the supplied function."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As an example, consider a situation in which you need to calculate the price after tax for a list of transactions:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "txns = [1.09, 23.56, 57.84, 4.56, 6.78]\n",
+ "TAX_RATE = .08"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_price_with_tax(txn):\n",
+ " return txn * (1 + TAX_RATE)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[1.1772000000000002, 25.4448, 62.467200000000005, 4.9248, 7.322400000000001]"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "final_prices = map(get_price_with_tax, txns)\n",
+ "list(final_prices)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, you have an iterable txns and a function `get_price_with_tax()`. You pass both of these arguments to `map()`, and store the resulting object in final_prices. You can easily convert this map object into a list using `list()`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Using List Comprehensions [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "List comprehensions are a third way of making lists. With this elegant approach, you could rewrite the for loop from the first example in just a single line of code:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 87,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]"
+ ]
+ },
+ "execution_count": 87,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "squares = [i * i for i in range(10)]\n",
+ "squares"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Rather than creating an empty list and adding each element to the end, you simply define the list and its contents at the same time by following this format:\n",
+ "\n",
+ "```python\n",
+ "new_list = [expression for member in iterable]\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Every list comprehension in Python includes three elements:\n",
+ "\n",
+ "- **expression** is the member itself, a call to a method, or any other valid expression that returns a value. In the example above, the expression `i * i` is the square of the member value.\n",
+ "- **member** is the object or value in the list or iterable. In the example above, the member value is `i`.\n",
+ "- **iterable** is a list, set, sequence, generator, or any other object that can return its elements one at a time. In the example above, the iterable is `range(10)`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Because the expression requirement is so flexible, a list comprehension in Python works well in many places where you would use `map()`. You can rewrite the pricing example with its own list comprehension:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "txns = [1.09, 23.56, 57.84, 4.56, 6.78]\n",
+ "TAX_RATE = .08"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_price_with_tax(txn):\n",
+ " return txn * (1 + TAX_RATE)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[1.1772000000000002, 25.4448, 62.467200000000005, 4.9248, 7.322400000000001]"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "final_prices = [get_price_with_tax(i) for i in txns]\n",
+ "final_prices"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The only distinction between this implementation and `map()` is that the list comprehension in Python returns a list, not a map object."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Benefits of Using List Comprehensions [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "List comprehensions are often described as being more Pythonic than loops or `map()`. But rather than blindly accepting that assessment, it’s worth it to understand the benefits of using a list comprehension in Python when compared to the alternatives. Later on, you’ll learn about a few scenarios where the alternatives are a better choice."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "One main benefit of using a list comprehension in Python is that it’s a single tool that you can use in many different situations. In addition to standard list creation, list comprehensions can also be used for mapping and filtering. You don’t have to use a different approach for each scenario."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is the main reason why list comprehensions are considered **Pythonic**, as Python embraces simple, powerful tools that you can use in a wide variety of situations. As an added side benefit, whenever you use a list comprehension in Python, you won’t need to remember the proper order of arguments like you would when you call `map()`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "List comprehensions are also more **declarative** than loops, which means they’re easier to read and understand. Loops require you to focus on how the list is created. You have to manually create an empty list, loop over the elements, and add each of them to the end of the list. With a list comprehension in Python, you can instead focus on what you want to go in the list and trust that Python will take care of how the list construction takes place."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## How to Supercharge Your Comprehensions [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In order to understand the full value that list comprehensions can provide, it’s helpful to understand their range of possible functionality. You’ll also want to understand the changes that are coming to the list comprehension in Python 3.8."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Using Conditional Logic [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Earlier, you saw this formula for how to create list comprehensions:\n",
+ "\n",
+ "```python\n",
+ "new_list = [expression for member in iterable]\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "While this formula is accurate, it’s also a bit incomplete. A more complete description of the comprehension formula adds support for optional **conditionals**. The most common way to add conditional logic to a list comprehension is to add a conditional to the end of the expression:\n",
+ "\n",
+ "```python\n",
+ "new_list = [expression for member in iterable (if conditional)]\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, your conditional statement comes just before the closing bracket."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Conditionals are important because they allow list comprehensions to filter out unwanted values, which would normally require a call to `filter()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['e', 'o', 'e', 'a', 'e', 'a', 'o', 'a']"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sentence = 'the rocket came back from mars'\n",
+ "vowels = [i for i in sentence if i in 'aeiou']\n",
+ "vowels"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this code block, the conditional statement filters out any characters in sentence that aren’t a vowel."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The conditional can test any valid expression. If you need a more complex filter, then you can even move the conditional logic to a separate function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sentence = 'The rocket, who was named Ted, came back \\\n",
+ "from Mars because he missed his friends.'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def is_consonant(letter):\n",
+ " vowels = 'aeiou'\n",
+ " return letter.isalpha() and letter.lower() not in vowels"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "consonants = [i for i in sentence if is_consonant(i)]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, you create a complex filter `is_consonant()` and pass this function as the conditional statement for your list comprehension. Note that the member value `i` is also passed as an argument to your function."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can place the conditional at the end of the statement for simple filtering, but what if you want to change a member value instead of filtering it out? In this case, it’s useful to place the conditional near the beginning of the expression:\n",
+ "\n",
+ "```python\n",
+ "new_list = [expression (if conditional) for member in iterable]\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "With this formula, you can use conditional logic to select from multiple possible output options. For example, if you have a list of prices, then you may want to replace negative prices with 0 and leave the positive values unchanged:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[1.25, 0, 10.22, 3.78, 0, 1.16]"
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "original_prices = [1.25, -9.45, 10.22, 3.78, -5.92, 1.16]\n",
+ "prices = [i if i > 0 else 0 for i in original_prices]\n",
+ "prices"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, your expression `i` contains a conditional statement, if `i > 0` else `0`. This tells Python to output the value of `i` if the number is positive, but to change `i` to `0` if the number is negative. If this seems overwhelming, then it may be helpful to view the conditional logic as its own function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[1.25, 0, 10.22, 3.78, 0, 1.16]"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "def get_price(price):\n",
+ " return price if price > 0 else 0\n",
+ "prices = [get_price(i) for i in original_prices]\n",
+ "prices"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now, your conditional statement is contained within `get_price()`, and you can use it as part of your list comprehension expression."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Using Set and Dictionary Comprehensions [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "While the list comprehension in Python is a common tool, you can also create set and dictionary comprehensions. A **set** comprehension is almost exactly the same as a list comprehension in Python. The difference is that set comprehensions make sure the output contains no duplicates. You can create a set comprehension by using curly braces instead of brackets:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 110,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'a', 'e', 'i', 'u'}"
+ ]
+ },
+ "execution_count": 110,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "quote = \"life, uh, finds a way\"\n",
+ "unique_vowels = {i for i in quote if i in 'aeiou'}\n",
+ "unique_vowels"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Your set comprehension outputs all the unique vowels it found in `quote`. Unlike lists, sets don’t guarantee that items will be saved in any particular order. This is why the first member of the set is `a`, even though the first vowel in quote is `i`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Dictionary comprehensions** are similar, with the additional requirement of defining a key:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 111,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}"
+ ]
+ },
+ "execution_count": 111,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "squares = {i: i * i for i in range(10)}\n",
+ "squares"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To create the squares dictionary, you use curly braces (`{}`) as well as a key-value pair (`i: i * i`) in your expression."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## When Not to Use a List Comprehension in Python [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "List comprehensions are useful and can help you write elegant code that’s easy to read and debug, but they’re not the right choice for all circumstances. They might make your code run more slowly or use more memory. If your code is less performant or harder to understand, then it’s probably better to choose an alternative."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Watch Out for Nested Comprehensions [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Comprehensions can be **nested** to create combinations of lists, dictionaries, and sets within a collection. For example, say a climate laboratory is tracking the high temperature in five different cities for the first week of June. The perfect data structure for storing this data could be a Python list comprehension nested within a dictionary comprehension:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'Austin': [0, 0, 0, 0, 0, 0, 0],\n",
+ " 'Tacoma': [0, 0, 0, 0, 0, 0, 0],\n",
+ " 'Topeka': [0, 0, 0, 0, 0, 0, 0],\n",
+ " 'Sacramento': [0, 0, 0, 0, 0, 0, 0],\n",
+ " 'Charlotte': [0, 0, 0, 0, 0, 0, 0]}"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "cities = ['Austin', 'Tacoma', 'Topeka', 'Sacramento', 'Charlotte']\n",
+ "temps = {city: [0 for _ in range(7)] for city in cities}\n",
+ "temps"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You create the outer collection temps with a dictionary comprehension. The expression is a key-value pair, which contains yet another comprehension. This code will quickly generate a list of data for each city in `cities`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Nested lists are a common way to create **matrices**, which are often used for mathematical purposes. Take a look at the code block below:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[[0, 1, 2, 3, 4],\n",
+ " [0, 1, 2, 3, 4],\n",
+ " [0, 1, 2, 3, 4],\n",
+ " [0, 1, 2, 3, 4],\n",
+ " [0, 1, 2, 3, 4],\n",
+ " [0, 1, 2, 3, 4]]"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "matrix = [[i for i in range(5)] for _ in range(6)]\n",
+ "matrix"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The outer list comprehension `[... for _ in range(6)]` creates six rows, while the inner list comprehension `[i for i in range(5)]` fills each of these rows with values."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So far, the purpose of each nested comprehension is pretty intuitive. However, there are other situations, such as **flattening** nested lists, where the logic arguably makes your code more confusing. Take this example, which uses a nested list comprehension to flatten a matrix:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0,\n",
+ " 1,\n",
+ " 2,\n",
+ " 3,\n",
+ " 4,\n",
+ " 0,\n",
+ " 1,\n",
+ " 2,\n",
+ " 3,\n",
+ " 4,\n",
+ " 0,\n",
+ " 1,\n",
+ " 2,\n",
+ " 3,\n",
+ " 4,\n",
+ " 0,\n",
+ " 1,\n",
+ " 2,\n",
+ " 3,\n",
+ " 4,\n",
+ " 0,\n",
+ " 1,\n",
+ " 2,\n",
+ " 3,\n",
+ " 4,\n",
+ " 0,\n",
+ " 1,\n",
+ " 2,\n",
+ " 3,\n",
+ " 4]"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "flat = [num for row in matrix for num in row]\n",
+ "flat"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The code to flatten the matrix is concise, but it may not be so intuitive to understand how it works. On the other hand, if you were to use `for` loops to flatten the same matrix, then your code will be much more straightforward:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "matrix = [\n",
+ " [0, 0, 0],\n",
+ " [1, 1, 1],\n",
+ " [2, 2, 2],\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "flat = []\n",
+ "for row in matrix:\n",
+ " for num in row:\n",
+ " flat.append(num)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0, 0, 0, 1, 1, 1, 2, 2, 2]"
+ ]
+ },
+ "execution_count": 27,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "flat"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now you can see that the code traverses one row of the matrix at a time, pulling out all the elements in that row before moving on to the next one."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "While the single-line nested list comprehension might seem more Pythonic, what’s most important is to write code that your team can easily understand and modify. When you choose your approach, you’ll have to make a judgment call based on whether you think the comprehension helps or hurts readability."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this tutorial, you learned how to use a list comprehension in Python to accomplish complex tasks without making your code overly complicated."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now you can:\n",
+ "\n",
+ "- Simplify loops and `map()` calls with declarative **list comprehensions**\n",
+ "- Supercharge your comprehensions with **conditional logic**\n",
+ "- Create **set** and **dictionary** comprehensions\n",
+ "- Determine when code clarity or performance dictates an **alternative approach**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Whenever you have to choose a list creation method, try multiple implementations and consider what’s easiest to read and understand in your specific scenario. If performance is important, then you can use profiling tools to give you actionable data instead of relying on hunches or guesses about what works the best."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Remember that while Python list comprehensions get a lot of attention, your intuition and ability to use data when it counts will help you write clean code that serves the task at hand. This, ultimately, is the key to making your code Pythonic!"
+ ]
+ }
+ ],
+ "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.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/python/01. Basics/15 Basic Input, Output, and String Formatting in Python.ipynb b/Python/01. Basics/16 Basic Input, Output, and String Formatting in Python.ipynb
similarity index 84%
rename from python/01. Basics/15 Basic Input, Output, and String Formatting in Python.ipynb
rename to Python/01. Basics/16 Basic Input, Output, and String Formatting in Python.ipynb
index d7a00ef..b82e069 100755
--- a/python/01. Basics/15 Basic Input, Output, and String Formatting in Python.ipynb
+++ b/Python/01. Basics/16 Basic Input, Output, and String Formatting in Python.ipynb
@@ -4,7 +4,34 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Basic Input, Output, and String Formatting in Python"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Basic Input, Output, and String Formatting in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [Reading Input From the Keyboard](#reading_input_from_the_keyboard)\n",
+ "* [Writing Output to the Console](#writing_output_to_the_console)\n",
+ " * [Unformatted Console Output](#unformatted_console_output)\n",
+ " * [Keyword Arguments to `print()`](#keyword_arguments_to_`print()`)\n",
+ " * [The `sep=` Keyword Argument](#the_`sep=`_keyword_argument)\n",
+ " * [The `end=` Keyword Argument](#the_`end=`_keyword_argument)\n",
+ " * [Output Stream Keyword Arguments](#output_stream_keyword_arguments)\n",
+ "* [Formatted String Output](#formatted_string_output)\n",
+ "\n",
+ "---"
]
},
{
@@ -36,7 +63,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Reading Input From the Keyboard"
+ "\n",
+ "## Reading Input From the Keyboard [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -143,7 +172,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 60,
"metadata": {},
"outputs": [
{
@@ -160,7 +189,7 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 61,
"metadata": {},
"outputs": [
{
@@ -170,7 +199,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: can only concatenate str (not \"int\") to str"
]
}
@@ -239,7 +268,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Writing Output to the Console"
+ "\n",
+ "## Writing Output to the Console [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -253,7 +284,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Unformatted Console Output\n",
+ "\n",
+ "### Unformatted Console Output [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"To display objects to the console, pass them as a comma-separated list of arguments to `print()`."
]
},
@@ -411,7 +445,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Keyword Arguments to `print()`\n",
+ "\n",
+ "### Keyword Arguments to `print()` [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"\n",
"`print()` takes a few additional arguments that provide modest control over the format of the output. Each of these is a special type of argument called a **keyword argument**. This introductory series will include a section on functions and parameter passing so you can learn more about keyword arguments."
]
@@ -454,7 +491,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### The `sep=` Keyword Argument"
+ "\n",
+ "#### The `sep=` Keyword Argument [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -571,7 +610,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### The `end=` Keyword Argument\n",
+ "\n",
+ "#### The `end=` Keyword Argument [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"The keyword argument `end=` causes output to be terminated by `` instead of the default newline:"
]
},
@@ -661,7 +703,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Output Stream Keyword Arguments\n",
+ "\n",
+ "### Output Stream Keyword Arguments [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"`print()` accepts two additional keyword arguments, both of which affect handling of the output stream:\n",
"\n",
"- `file=`: By default, `print()` sends its output to a default stream called `sys.stdout`, which is usually equivalent to the console. The `file=` argument causes output to be sent to an alternate stream designated by `` instead.\n",
@@ -680,7 +725,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Formatted String Output"
+ "\n",
+ "## Formatted String Output [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -721,7 +768,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/16 Newer Python String Format Techniques.ipynb b/Python/01. Basics/17 Newer Python String Format Techniques.ipynb
similarity index 90%
rename from python/01. Basics/16 Newer Python String Format Techniques.ipynb
rename to Python/01. Basics/17 Newer Python String Format Techniques.ipynb
index 2b437d4..31d0cd0 100755
--- a/python/01. Basics/16 Newer Python String Format Techniques.ipynb
+++ b/Python/01. Basics/17 Newer Python String Format Techniques.ipynb
@@ -2,15 +2,56 @@
"cells": [
{
"cell_type": "markdown",
- "id": "6fc28563",
"metadata": {},
"source": [
- "# Newer Python String Format Techniques"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Newer Python String Format Techniques \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [The Python String `.format()` Method](#the_python_string_`.format()`_method)\n",
+ "* [The String `.format()` Method: Arguments](#the_string_`.format()`_method:_arguments)\n",
+ " * [Positional Arguments](#positional_arguments)\n",
+ " * [Keyword Arguments](#keyword_arguments)\n",
+ "* [The String `.format()` Method: Simple Replacement Fields](#the_string_`.format()`_method:_simple_replacement_fields)\n",
+ " * [The `` Component](#the_``_component)\n",
+ " * [The `` Component](#the_``_component)\n",
+ " * [The `` Component](#the_``_component)\n",
+ " * [The `` Subcomponent](#the_``_subcomponent)\n",
+ " * [The `` and `` Subcomponents](#the_``_and_``_subcomponents)\n",
+ " * [The `` Subcomponent](#the_``_subcomponent)\n",
+ " * [The `#`` Subcomponent](#the_`#``_subcomponent)\n",
+ " * [The `0 `Subcomponent](#the_`0_`subcomponent)\n",
+ " * [The `` Subcomponent](#the_``_subcomponent)\n",
+ " * [The `` Subcomponent](#the_``_subcomponent)\n",
+ " * [The `.` Subcomponent](#the_`.`_subcomponent)\n",
+ "* [The Python Formatted String Literal (f-String)](#the_python_formatted_string_literal_(f-string))\n",
+ " * [Why `str.format()` Isn’t Great](#why_`str.format()`_isn’t_great)\n",
+ " * [f-String Syntax](#f-string_syntax)\n",
+ " * [f-String Expression Limitations](#f-string_expression_limitations)\n",
+ " * [f-String Formatting](#f-string_formatting)\n",
+ "* [Python f-string benchmarks](#python_f-string_benchmarks)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "* [Further Reading](#further_reading)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "aed78bcd",
"metadata": {},
"source": [
"The **string modulo operator** is useful, and it’s good for you to be familiar with it because you’re likely to encounter it in older Python code. However, there are two newer ways that you can use Python to format strings that are arguably more preferable."
@@ -18,7 +59,6 @@
},
{
"cell_type": "markdown",
- "id": "fa853e41",
"metadata": {},
"source": [
"In this section, you’ll learn about:\n",
@@ -29,7 +69,6 @@
},
{
"cell_type": "markdown",
- "id": "0ad988e6",
"metadata": {},
"source": [
"You’ll learn about these formatting techniques in detail and add them to your Python string formatting toolkit. Note that there’s a standard module called string containing a class called `Template`, which provides some `string` formatting through interpolation. The string modulo operator provides more or less the same functionality, so you won’t cover `string.Template` here."
@@ -37,10 +76,12 @@
},
{
"cell_type": "markdown",
- "id": "08ad6a1c",
"metadata": {},
"source": [
- "## The Python String `.format()` Method\n",
+ "\n",
+ "## The Python String `.format()` Method [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"The Python string `.format()` method was introduced in version 2.6. It’s similar in many ways to the string modulo operator, but `.format()` goes well beyond in versatility. The general form of a Python `.format()` call is shown below:\n",
"\n",
"```python\n",
@@ -50,7 +91,6 @@
},
{
"cell_type": "markdown",
- "id": "7fc376ad",
"metadata": {},
"source": [
"Note that this is a method, not an operator (You will learn methods in details in OOP, for now just know that you call methods on objects). You call the method on ``, which is a string containing replacement fields. The `` and `` to the method specify values that are inserted into `` in place of the replacement fields. The resulting formatted string is the method’s return value."
@@ -58,7 +98,6 @@
},
{
"cell_type": "markdown",
- "id": "942ee05b",
"metadata": {},
"source": [
"In the `` string, replacement fields are enclosed in curly braces (`{}`). Anything not contained in curly braces is literal text that’s copied directly from the template to the output. If you need to include a literal curly bracket character, like `{` or `}`, in the template string, then you can escape this character by doubling it:"
@@ -66,8 +105,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "id": "cbf466b2",
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
@@ -76,7 +114,7 @@
"'{ foo }'"
]
},
- "execution_count": 2,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -87,7 +125,6 @@
},
{
"cell_type": "markdown",
- "id": "30cd12d0",
"metadata": {},
"source": [
"Now the curly braces are included in your output."
@@ -95,15 +132,15 @@
},
{
"cell_type": "markdown",
- "id": "bd276311",
"metadata": {},
"source": [
- "## The String `.format()` Method: Arguments"
+ "\n",
+ "## The String `.format()` Method: Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "ca28f2b9",
"metadata": {},
"source": [
"Let’s start with a quick example to get you acquainted before you dive into more detail on how to use this method in Python to format strings."
@@ -111,7 +148,6 @@
},
{
"cell_type": "markdown",
- "id": "5c1cc937",
"metadata": {},
"source": [
"Here’s an example from on how to use the string modulo operator (You don't have to learn it):"
@@ -119,8 +155,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
- "id": "a0b37f8d",
+ "execution_count": 1,
"metadata": {},
"outputs": [
{
@@ -137,7 +172,6 @@
},
{
"cell_type": "markdown",
- "id": "2de184d2",
"metadata": {},
"source": [
"Here, you used the string modulo operator in Python to format the string. Now, you can use Python’s string `.format()` method to obtain the same result, like this:"
@@ -146,7 +180,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "bdf9bb86",
"metadata": {},
"outputs": [
{
@@ -163,7 +196,6 @@
},
{
"cell_type": "markdown",
- "id": "0a405f67",
"metadata": {},
"source": [
"In this example, `` is the string `'{0} {1} cost ${2}'`. The replacement fields are `{0}`, `{1}`, and `{2}`, which contain numbers that correspond to the zero-based positional arguments `6`, `'bananas'`, and `1.74`. Each positional argument is inserted into the template in place of its corresponding replacement field:"
@@ -171,7 +203,6 @@
},
{
"cell_type": "markdown",
- "id": "db7f8abc",
"metadata": {},
"source": [
"
"
@@ -179,7 +210,6 @@
},
{
"cell_type": "markdown",
- "id": "7cf83953",
"metadata": {},
"source": [
"The next example uses **keyword arguments** (you will learn about keyword and positional arguments in the section on defining your own functions) instead of positional parameters to produce the same result:"
@@ -188,7 +218,6 @@
{
"cell_type": "code",
"execution_count": 34,
- "id": "48109412",
"metadata": {},
"outputs": [
{
@@ -209,7 +238,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "a1a765b8",
"metadata": {},
"outputs": [],
"source": []
@@ -217,7 +245,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "68a34eb3",
"metadata": {},
"outputs": [
{
@@ -240,7 +267,6 @@
},
{
"cell_type": "markdown",
- "id": "a2f371bb",
"metadata": {},
"source": [
"In this case, the replacement fields are `{quantity}`, `{item}`, and `{price}`. These fields specify keywords that correspond to the keyword arguments `quantity=6`, `item='bananas'`, and `price=1.74`. Each keyword value is inserted into the template in place of its corresponding replacement field:"
@@ -248,7 +274,6 @@
},
{
"cell_type": "markdown",
- "id": "8b32562c",
"metadata": {},
"source": [
"
"
@@ -256,7 +281,6 @@
},
{
"cell_type": "markdown",
- "id": "fe2f1ce4",
"metadata": {},
"source": [
"You’ll learn more about positional and keywords arguments there in the next section in this introductory series, which explores functions and argument passing. For now, the two sections that follow will show you how these are used with the Python `.format()` method."
@@ -264,15 +288,15 @@
},
{
"cell_type": "markdown",
- "id": "dc366ec8",
"metadata": {},
"source": [
- "### Positional Arguments"
+ "\n",
+ "### Positional Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "3751c87e",
"metadata": {},
"source": [
"Positional arguments are inserted into the template in place of **numbered replacement fields**. Like list indexing, the numbering of replacement fields is zero-based. The first positional argument is numbered `0`, the second is numbered `1`, and so on:"
@@ -281,7 +305,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "b2f8c7f6",
"metadata": {},
"outputs": [
{
@@ -301,7 +324,6 @@
},
{
"cell_type": "markdown",
- "id": "0ceec72d",
"metadata": {},
"source": [
"Note that replacement fields don’t have to appear in the template in numerical order. They can be specified in any order, and they can appear more than once:"
@@ -310,7 +332,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "2154e517",
"metadata": {},
"outputs": [
{
@@ -330,7 +351,6 @@
},
{
"cell_type": "markdown",
- "id": "13dedfdb",
"metadata": {},
"source": [
"When you specify a replacement field number that’s out of range, you’ll get an error. In the following example, the positional arguments are numbered `0`, `1`, and `2`, but you specify `{3}` in the template:"
@@ -339,7 +359,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "e4cbb08b",
"metadata": {},
"outputs": [
{
@@ -360,7 +379,6 @@
},
{
"cell_type": "markdown",
- "id": "8ecb9cd6",
"metadata": {},
"source": [
"This raises an `IndexError` exception (There will be a dedicated section on Exceptions in python)."
@@ -368,7 +386,6 @@
},
{
"cell_type": "markdown",
- "id": "ecf024dc",
"metadata": {},
"source": [
"Starting with Python 3.1, you can omit the numbers in the replacement fields, in which case the interpreter assumes sequential order. This is referred to as **automatic field numbering**:"
@@ -377,7 +394,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "64d43109",
"metadata": {},
"outputs": [
{
@@ -397,7 +413,6 @@
},
{
"cell_type": "markdown",
- "id": "d2685d4a",
"metadata": {},
"source": [
"When you specify automatic field numbering, you must provide at least as many arguments as there are replacement fields:"
@@ -406,7 +421,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "fc2ed62a",
"metadata": {},
"outputs": [
{
@@ -427,7 +441,6 @@
},
{
"cell_type": "markdown",
- "id": "24fb7f2b",
"metadata": {},
"source": [
"In this case, there are four replacement fields in the template but only three arguments, so an `IndexError` exception occurs. On the other hand, it’s fine if the arguments outnumber the replacement fields. The excess arguments simply aren’t used:"
@@ -436,7 +449,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "b1233b59",
"metadata": {},
"outputs": [
{
@@ -456,7 +468,6 @@
},
{
"cell_type": "markdown",
- "id": "df7a9717",
"metadata": {},
"source": [
"Here, the argument 'baz' is ignored."
@@ -464,7 +475,6 @@
},
{
"cell_type": "markdown",
- "id": "1a5a5674",
"metadata": {},
"source": [
"Note that you can’t intermingle these two techniques:"
@@ -473,7 +483,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "83c65418",
"metadata": {},
"outputs": [
{
@@ -494,7 +503,6 @@
},
{
"cell_type": "markdown",
- "id": "89af581f",
"metadata": {},
"source": [
"When you use Python to format strings with positional arguments, you must choose between either automatic or explicit replacement field numbering."
@@ -502,15 +510,15 @@
},
{
"cell_type": "markdown",
- "id": "c24c23ec",
"metadata": {},
"source": [
- "### Keyword Arguments"
+ "\n",
+ "### Keyword Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "1b9531ef",
"metadata": {},
"source": [
"Keyword arguments are inserted into the template string in place of **keyword replacement fields with the same name**:"
@@ -518,8 +526,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
- "id": "0efdab61",
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
@@ -528,7 +535,7 @@
"'foo/bar/baz'"
]
},
- "execution_count": 17,
+ "execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
@@ -539,7 +546,6 @@
},
{
"cell_type": "markdown",
- "id": "b1081f5c",
"metadata": {},
"source": [
"In this example, the values of the keyword arguments `x`, `y`, and `z` take the place of the replacement fields `{x}`, `{y}`, and `{z}`, respectively."
@@ -547,7 +553,6 @@
},
{
"cell_type": "markdown",
- "id": "5503004b",
"metadata": {},
"source": [
"If you refer to a keyword argument that’s missing, then you’ll see an error:"
@@ -556,7 +561,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "9def6418",
"metadata": {},
"outputs": [
{
@@ -577,7 +581,6 @@
},
{
"cell_type": "markdown",
- "id": "d9e1efeb",
"metadata": {},
"source": [
"Here, you specify replacement field `{w}`, but there’s no corresponding keyword argument named `w`. Python raises a `KeyError` exception."
@@ -585,7 +588,6 @@
},
{
"cell_type": "markdown",
- "id": "dd2f797a",
"metadata": {},
"source": [
"While you have to specify positional arguments in sequential order, but you can specify keyword arguments in any arbitrary order:"
@@ -594,7 +596,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "7931d15f",
"metadata": {},
"outputs": [
{
@@ -615,7 +616,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "496372f0",
"metadata": {},
"outputs": [
{
@@ -636,7 +636,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "fb010d3b",
"metadata": {},
"outputs": [
{
@@ -657,7 +656,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "a981094b",
"metadata": {},
"outputs": [
{
@@ -677,7 +675,6 @@
},
{
"cell_type": "markdown",
- "id": "04113290",
"metadata": {},
"source": [
"You can specify both positional and keyword arguments in one Python `.format()` call. Just note that, if you do so, then all the **positional arguments must appear before any of the keyword arguments**:"
@@ -686,7 +683,6 @@
{
"cell_type": "code",
"execution_count": 23,
- "id": "f1714489",
"metadata": {},
"outputs": [
{
@@ -707,7 +703,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "6cb4c4ac",
"metadata": {},
"outputs": [
{
@@ -725,7 +720,6 @@
},
{
"cell_type": "markdown",
- "id": "2d84ce4e",
"metadata": {},
"source": [
"In fact, the requirement that all positional arguments appear before any keyword arguments doesn’t apply only to Python format methods. This is generally true of any function or method call. You’ll learn more about this in the next section in this series, which explores functions and function calls."
@@ -733,7 +727,6 @@
},
{
"cell_type": "markdown",
- "id": "f95d1afb",
"metadata": {},
"source": [
"In all the examples shown so far, the values you passed to the Python `.format()` method have been literal values, but you may specify variables as well:"
@@ -742,7 +735,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "a1679e12",
"metadata": {},
"outputs": [],
"source": [
@@ -754,7 +746,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "f48454bf",
"metadata": {},
"outputs": [
{
@@ -774,7 +765,6 @@
},
{
"cell_type": "markdown",
- "id": "8b60150d",
"metadata": {},
"source": [
"In this case, you pass the variables `x` and `y` as positional parameter values and `z` as a keyword parameter value."
@@ -782,15 +772,15 @@
},
{
"cell_type": "markdown",
- "id": "39f54364",
"metadata": {},
"source": [
- "## The String `.format()` Method: Simple Replacement Fields"
+ "\n",
+ "## The String `.format()` Method: Simple Replacement Fields [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "c27f6845",
"metadata": {},
"source": [
"As you’ve seen, when you call Python’s `.format()` method, the `` string contains replacement fields. These indicate where in the template to insert the arguments to the method. A replacement field consists of three components:\n",
@@ -800,7 +790,6 @@
},
{
"cell_type": "markdown",
- "id": "627c76ab",
"metadata": {},
"source": [
"These components are interpreted as follows:\n",
@@ -814,7 +803,6 @@
},
{
"cell_type": "markdown",
- "id": "a5fde286",
"metadata": {},
"source": [
"Each component is optional and may be omitted. Let’s take a look at each component in more depth."
@@ -822,15 +810,15 @@
},
{
"cell_type": "markdown",
- "id": "c40d44f2",
"metadata": {},
"source": [
- "### The `` Component"
+ "`_component\">\n",
+ "### The `` Component [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "62621a97",
"metadata": {},
"source": [
"The `` component is the first portion of a replacement field:\n",
@@ -840,7 +828,6 @@
},
{
"cell_type": "markdown",
- "id": "4bf47386",
"metadata": {},
"source": [
"`` indicates which argument from the argument list is inserted into the Python format string in the given location. It’s either a number for a positional argument or a keyword for a keyword argument. In the following example, the `` components of the replacement fields are `0`, `1`, and `baz`, respectively:"
@@ -849,7 +836,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "711eeb38",
"metadata": {},
"outputs": [
{
@@ -870,7 +856,6 @@
},
{
"cell_type": "markdown",
- "id": "cdde8790",
"metadata": {},
"source": [
"If an argument is a list, then you can use indices with to access the list’s elements:"
@@ -879,7 +864,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "2966c55f",
"metadata": {},
"outputs": [],
"source": [
@@ -889,7 +873,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "1de7be56",
"metadata": {},
"outputs": [
{
@@ -911,7 +894,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "faff1eea",
"metadata": {},
"outputs": [
{
@@ -932,7 +914,6 @@
},
{
"cell_type": "markdown",
- "id": "5b83a3c1",
"metadata": {},
"source": [
"Similarly, you can use a key reference with `` if the corresponding argument is a dictionary:"
@@ -941,7 +922,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "af28bb52",
"metadata": {},
"outputs": [],
"source": [
@@ -951,7 +931,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "2b595e80",
"metadata": {},
"outputs": [
{
@@ -972,7 +951,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "8d0b0d6a",
"metadata": {},
"outputs": [
{
@@ -994,7 +972,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "453fd789",
"metadata": {},
"outputs": [
{
@@ -1015,7 +992,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "54d04866",
"metadata": {},
"outputs": [
{
@@ -1035,7 +1011,6 @@
},
{
"cell_type": "markdown",
- "id": "b84d7b1c",
"metadata": {},
"source": [
"You can also reference **object attributes** from within a replacement field. In the previous tutorial in this series, you learned that virtually every item of data in Python is an **object**. Objects may have **attributes** assigned to them that are accessed using dot notation:\n",
@@ -1047,7 +1022,6 @@
},
{
"cell_type": "markdown",
- "id": "0d9c8cff",
"metadata": {},
"source": [
"Here, `obj` is an object with an attribute named `attr`. You use dot notation to access the object’s attribute. Let’s see an example. Complex numbers in Python have attributes named `.real` and `.imag` that represent the real and imaginary portions of the number. You can access these using dot notation as well:"
@@ -1056,7 +1030,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "c8bb8042",
"metadata": {},
"outputs": [],
"source": [
@@ -1066,7 +1039,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "28a82c4d",
"metadata": {},
"outputs": [
{
@@ -1087,7 +1059,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "3098e228",
"metadata": {},
"outputs": [
{
@@ -1108,7 +1079,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "002c339c",
"metadata": {},
"outputs": [
{
@@ -1128,7 +1098,6 @@
},
{
"cell_type": "markdown",
- "id": "5d6e0de0",
"metadata": {},
"source": [
"There are several upcoming tutorials in this series on object-oriented programming, in which you’ll learn a great deal more about object attributes like these."
@@ -1136,7 +1105,6 @@
},
{
"cell_type": "markdown",
- "id": "b0ead2db",
"metadata": {},
"source": [
"The relevance of object attributes in this context is that you can specify them in a Python `.format()` replacement field:"
@@ -1145,7 +1113,6 @@
{
"cell_type": "code",
"execution_count": 41,
- "id": "22df7851",
"metadata": {},
"outputs": [
{
@@ -1166,7 +1133,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "27e95802",
"metadata": {},
"outputs": [
{
@@ -1186,7 +1152,6 @@
},
{
"cell_type": "markdown",
- "id": "70894fa7",
"metadata": {},
"source": [
"As you can see, it’s relatively straightforward in Python to format components of complex objects using the `.format()` method."
@@ -1194,15 +1159,15 @@
},
{
"cell_type": "markdown",
- "id": "a8d3b298",
"metadata": {},
"source": [
- "### The `` Component"
+ "`_component\">\n",
+ "### The `` Component [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "70581860",
"metadata": {},
"source": [
"The `` component is the middle portion of a replacement field:\n",
@@ -1212,7 +1177,6 @@
},
{
"cell_type": "markdown",
- "id": "e7f9e15a",
"metadata": {},
"source": [
"Python can format an object as a string using three different built-in functions:\n",
@@ -1224,7 +1188,6 @@
},
{
"cell_type": "markdown",
- "id": "8e3cdddb",
"metadata": {},
"source": [
"By default, the Python `.format()` method uses `str()`, but in some instances, you may want to force `.format()` to use one of the other two. You can do this with the `` component of a replacement field. The possible values for `` are shown in the table below:\n",
@@ -1238,7 +1201,6 @@
},
{
"cell_type": "markdown",
- "id": "7ec33325",
"metadata": {},
"source": [
"The following examples force Python to perform string conversion using `str()`, `repr()`, and `ascii()`, respectively:"
@@ -1246,8 +1208,7 @@
},
{
"cell_type": "code",
- "execution_count": 44,
- "id": "c8ba57ad",
+ "execution_count": 39,
"metadata": {},
"outputs": [
{
@@ -1256,7 +1217,7 @@
"'42'"
]
},
- "execution_count": 44,
+ "execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
@@ -1267,8 +1228,7 @@
},
{
"cell_type": "code",
- "execution_count": 45,
- "id": "d877eca0",
+ "execution_count": 40,
"metadata": {},
"outputs": [
{
@@ -1277,7 +1237,7 @@
"'42'"
]
},
- "execution_count": 45,
+ "execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
@@ -1288,8 +1248,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
- "id": "117947b6",
+ "execution_count": 41,
"metadata": {},
"outputs": [
{
@@ -1298,7 +1257,7 @@
"'42'"
]
},
- "execution_count": 1,
+ "execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
@@ -1309,7 +1268,6 @@
},
{
"cell_type": "markdown",
- "id": "beae2936",
"metadata": {},
"source": [
"In many cases, the result is the same regardless of which conversion function you use, as you can see in the example above. That being said, you won’t often need the `` component, so you won’t spend a lot of time on it here. However, there are situations where it makes a difference, so it’s good to be aware that you have the capability to force a specific conversion function if you need to."
@@ -1317,15 +1275,15 @@
},
{
"cell_type": "markdown",
- "id": "ac92d9db",
"metadata": {},
"source": [
- "### The `` Component"
+ "`_component\">\n",
+ "### The `` Component [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "f1c9f715",
"metadata": {},
"source": [
"The `` component is the last portion of a replacement field:\n",
@@ -1335,7 +1293,6 @@
},
{
"cell_type": "markdown",
- "id": "b5d7bbf9",
"metadata": {},
"source": [
"`` represents the guts of the Python `.format()` method’s functionality. It contains information that exerts fine control over how values are formatted prior to being inserted into the template string. The general form is this:\n",
@@ -1345,7 +1302,6 @@
},
{
"cell_type": "markdown",
- "id": "69d4bd9e",
"metadata": {},
"source": [
"The ten subcomponents of `` are specified in the order shown. They control formatting as described in the table below:\n",
@@ -1366,7 +1322,6 @@
},
{
"cell_type": "markdown",
- "id": "046dab86",
"metadata": {},
"source": [
"These functions are analogous to the components you’ll find in the string modulo operator’s conversion specifier, but with somewhat greater capability. You’ll see their capabilities explained more fully in the following sections."
@@ -1374,15 +1329,15 @@
},
{
"cell_type": "markdown",
- "id": "143ae8a3",
"metadata": {},
"source": [
- "#### The `` Subcomponent"
+ "`_subcomponent\">\n",
+ "#### The `` Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "aaf3c192",
"metadata": {},
"source": [
"Let’s start with ``, which is the final portion of ``. The `` subcomponent specifies the presentation type, which is the type of conversion that’s performed on the corresponding value to produce the output. The possible values are shown below:\n",
@@ -1404,7 +1359,6 @@
},
{
"cell_type": "markdown",
- "id": "99878c89",
"metadata": {},
"source": [
"These are like the conversion types used with the string modulo operator, and in many cases, they function the same."
@@ -1413,7 +1367,6 @@
{
"cell_type": "code",
"execution_count": 47,
- "id": "4b0aed60",
"metadata": {},
"outputs": [
{
@@ -1434,7 +1387,6 @@
{
"cell_type": "code",
"execution_count": 48,
- "id": "7cdc490c",
"metadata": {},
"outputs": [
{
@@ -1455,7 +1407,6 @@
{
"cell_type": "code",
"execution_count": 49,
- "id": "5516eedf",
"metadata": {},
"outputs": [
{
@@ -1475,8 +1426,7 @@
},
{
"cell_type": "code",
- "execution_count": 50,
- "id": "f966f6aa",
+ "execution_count": 57,
"metadata": {},
"outputs": [
{
@@ -1485,7 +1435,7 @@
"'1f'"
]
},
- "execution_count": 50,
+ "execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
@@ -1496,15 +1446,15 @@
},
{
"cell_type": "markdown",
- "id": "4a0220f9",
"metadata": {},
"source": [
- "#### The `` and `` Subcomponents"
+ "`_and_``_subcomponents\">\n",
+ "#### The `` and `` Subcomponents [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "b8a2ff58",
"metadata": {},
"source": [
"`` and `` control how formatted output is padded and positioned within the specified field width. These subcomponents only have meaning when the formatted field value doesn’t occupy the entire field width, which can only happen if a minimum field width is specified with ``. If `` isn’t specified, then `` and `` are effectively ignored. You’ll cover `` later on in this tutorial."
@@ -1512,7 +1462,6 @@
},
{
"cell_type": "markdown",
- "id": "b4f45021",
"metadata": {},
"source": [
"Here are the possible values for the `` subcomponent:\n",
@@ -1525,7 +1474,6 @@
},
{
"cell_type": "markdown",
- "id": "f72176d7",
"metadata": {},
"source": [
"A value using the less than sign (`<`) indicates that the output is left-justified:"
@@ -1534,7 +1482,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "c5d026c3",
"metadata": {},
"outputs": [
{
@@ -1555,7 +1502,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "e4db895c",
"metadata": {},
"outputs": [
{
@@ -1575,7 +1521,6 @@
},
{
"cell_type": "markdown",
- "id": "56956b97",
"metadata": {},
"source": [
"This behavior is the default for string values."
@@ -1583,7 +1528,6 @@
},
{
"cell_type": "markdown",
- "id": "66384f5b",
"metadata": {},
"source": [
"A value using the greater than sign (`>`) indicates that the output should be right-justified:"
@@ -1591,8 +1535,7 @@
},
{
"cell_type": "code",
- "execution_count": 53,
- "id": "84f064f0",
+ "execution_count": 59,
"metadata": {},
"outputs": [
{
@@ -1601,7 +1544,7 @@
"' foo'"
]
},
- "execution_count": 53,
+ "execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
@@ -1613,7 +1556,6 @@
{
"cell_type": "code",
"execution_count": 55,
- "id": "ec19cbd2",
"metadata": {},
"outputs": [
{
@@ -1633,7 +1575,6 @@
},
{
"cell_type": "markdown",
- "id": "5524c309",
"metadata": {},
"source": [
"This behavior is the default for numeric values."
@@ -1641,7 +1582,6 @@
},
{
"cell_type": "markdown",
- "id": "654d5bea",
"metadata": {},
"source": [
"A value using a caret (`^`) indicates that the output should be centered in the output field:"
@@ -1649,29 +1589,27 @@
},
{
"cell_type": "code",
- "execution_count": 57,
- "id": "15872a41",
+ "execution_count": 64,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "' foo '"
+ "' foo '"
]
},
- "execution_count": 57,
+ "execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "'{0:^8s}'.format('foo')"
+ "'{0:^10s}'.format('foo')"
]
},
{
"cell_type": "code",
- "execution_count": 58,
- "id": "e0721804",
+ "execution_count": 65,
"metadata": {},
"outputs": [
{
@@ -1680,7 +1618,7 @@
"' 123 '"
]
},
- "execution_count": 58,
+ "execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
@@ -1691,7 +1629,6 @@
},
{
"cell_type": "markdown",
- "id": "64e0352f",
"metadata": {},
"source": [
"Finally, you can also specify a value using the equals sign (`=`) for the `` subcomponent. This only has meaning for numeric values, and only when a sign is included in the output."
@@ -1699,7 +1636,6 @@
},
{
"cell_type": "markdown",
- "id": "0b19efe1",
"metadata": {},
"source": [
"When numeric output includes a sign, it’s normally placed directly to the left of the first digit in the number, as shown above. If `` is set to the equals sign (`=`), then the sign appears at the left edge of the output field, and padding is placed in between the sign and the number:"
@@ -1708,7 +1644,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "f3a878e8",
"metadata": {},
"outputs": [
{
@@ -1729,7 +1664,6 @@
{
"cell_type": "code",
"execution_count": 60,
- "id": "cd234bd1",
"metadata": {},
"outputs": [
{
@@ -1750,7 +1684,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "4cfb2877",
"metadata": {},
"outputs": [
{
@@ -1771,7 +1704,6 @@
{
"cell_type": "code",
"execution_count": 62,
- "id": "03cdc783",
"metadata": {},
"outputs": [
{
@@ -1791,7 +1723,6 @@
},
{
"cell_type": "markdown",
- "id": "83731d62",
"metadata": {},
"source": [
"You’ll cover the `` component in detail in the next section."
@@ -1799,7 +1730,6 @@
},
{
"cell_type": "markdown",
- "id": "f2425e80",
"metadata": {},
"source": [
"`` specifies how to fill in extra space when the formatted value doesn’t completely fill the output width. It can be any character except for curly braces (`{}`). (If you really feel compelled to pad a field with curly braces, then you’ll just have to find another way!)"
@@ -1807,7 +1737,6 @@
},
{
"cell_type": "markdown",
- "id": "ac8ef12e",
"metadata": {},
"source": [
"Some examples of the use of `` are shown below:"
@@ -1816,7 +1745,6 @@
{
"cell_type": "code",
"execution_count": 63,
- "id": "d7429e2b",
"metadata": {},
"outputs": [
{
@@ -1837,7 +1765,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "c8726cf0",
"metadata": {},
"outputs": [
{
@@ -1858,7 +1785,6 @@
{
"cell_type": "code",
"execution_count": 65,
- "id": "e52dbc98",
"metadata": {},
"outputs": [
{
@@ -1878,7 +1804,6 @@
},
{
"cell_type": "markdown",
- "id": "274e046e",
"metadata": {},
"source": [
"If you specify a value for ``, then you should also include a value for `` as well."
@@ -1887,7 +1812,6 @@
{
"cell_type": "code",
"execution_count": 67,
- "id": "42767767",
"metadata": {},
"outputs": [
{
@@ -1909,15 +1833,15 @@
},
{
"cell_type": "markdown",
- "id": "bab4c057",
"metadata": {},
"source": [
- "#### The `` Subcomponent"
+ "`_subcomponent\">\n",
+ "#### The `` Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "42e73c00",
"metadata": {},
"source": [
"You can control whether a sign appears in numeric output with the `` component. For example, in the following, the plus sign (`+`) specified in the `` indicates that the value should always be displayed with a leading sign:"
@@ -1926,7 +1850,6 @@
{
"cell_type": "code",
"execution_count": 68,
- "id": "a752e0c1",
"metadata": {},
"outputs": [
{
@@ -1947,7 +1870,6 @@
{
"cell_type": "code",
"execution_count": 69,
- "id": "4cfc957e",
"metadata": {},
"outputs": [
{
@@ -1967,7 +1889,6 @@
},
{
"cell_type": "markdown",
- "id": "5e502047",
"metadata": {},
"source": [
"Here, you use the plus sign (`+`), so a sign will always be included for both positive and negative values. If you use the minus sign (`-`), then only negative numeric values will include a leading sign, and positive values won’t:"
@@ -1976,7 +1897,6 @@
{
"cell_type": "code",
"execution_count": 70,
- "id": "68848bb1",
"metadata": {},
"outputs": [
{
@@ -1997,7 +1917,6 @@
{
"cell_type": "code",
"execution_count": 71,
- "id": "322ccb24",
"metadata": {},
"outputs": [
{
@@ -2017,7 +1936,6 @@
},
{
"cell_type": "markdown",
- "id": "090fb2d2",
"metadata": {},
"source": [
"When you use a single space (`' '`), it means a sign is included for negative values, and an ASCII space character for positive values:"
@@ -2026,7 +1944,6 @@
{
"cell_type": "code",
"execution_count": 72,
- "id": "1f394e84",
"metadata": {},
"outputs": [
{
@@ -2046,8 +1963,7 @@
},
{
"cell_type": "code",
- "execution_count": 73,
- "id": "c79e2c15",
+ "execution_count": 68,
"metadata": {},
"outputs": [
{
@@ -2056,7 +1972,7 @@
"'***123'"
]
},
- "execution_count": 73,
+ "execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
@@ -2068,7 +1984,6 @@
{
"cell_type": "code",
"execution_count": 74,
- "id": "25749885",
"metadata": {},
"outputs": [
{
@@ -2088,7 +2003,6 @@
},
{
"cell_type": "markdown",
- "id": "652b45ec",
"metadata": {},
"source": [
"Since the space character is the default fill character, you’d only notice the effect of this if an alternate `` character is specified."
@@ -2096,7 +2010,6 @@
},
{
"cell_type": "markdown",
- "id": "3ddcf93f",
"metadata": {},
"source": [
"Lastly, recall from above that when you specify the equals sign (`=`) for `` and you include a `` specifier, the padding goes between the sign and the value, rather than to the left of the sign."
@@ -2104,15 +2017,15 @@
},
{
"cell_type": "markdown",
- "id": "a48ed507",
"metadata": {},
"source": [
- "#### The `#`` Subcomponent"
+ "\n",
+ "#### The `#`` Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "66a3060f",
"metadata": {},
"source": [
"When you specify a hash character (`#`) in ``, Python will select an alternate output form for certain presentation types. This is analogous to the `#` conversion flag for the string modulo operator. For binary, octal, and hexadecimal presentation types, the hash character (`#`) causes inclusion of an explicit base indicator to the left of the value:"
@@ -2121,7 +2034,6 @@
{
"cell_type": "code",
"execution_count": 81,
- "id": "2e4a52b2",
"metadata": {},
"outputs": [
{
@@ -2142,7 +2054,6 @@
{
"cell_type": "code",
"execution_count": 76,
- "id": "eb938f49",
"metadata": {},
"outputs": [
{
@@ -2163,7 +2074,6 @@
{
"cell_type": "code",
"execution_count": 77,
- "id": "3df4b226",
"metadata": {},
"outputs": [
{
@@ -2183,7 +2093,6 @@
},
{
"cell_type": "markdown",
- "id": "c4a1ecf0",
"metadata": {},
"source": [
"As you can see, the base indicator can be `0b`, `0o`, or `0x`."
@@ -2191,7 +2100,6 @@
},
{
"cell_type": "markdown",
- "id": "84d90099",
"metadata": {},
"source": [
"For floating-point or exponential presentation types, the hash character forces the output to contain a decimal point, even if the output consists of a whole number:"
@@ -2200,7 +2108,6 @@
{
"cell_type": "code",
"execution_count": 95,
- "id": "f1692667",
"metadata": {},
"outputs": [
{
@@ -2221,7 +2128,6 @@
{
"cell_type": "code",
"execution_count": 96,
- "id": "398340b5",
"metadata": {},
"outputs": [
{
@@ -2241,7 +2147,6 @@
},
{
"cell_type": "markdown",
- "id": "c34feb05",
"metadata": {},
"source": [
"For any presentation type other than those shown above, the hash character (`#`) has no effect."
@@ -2249,15 +2154,15 @@
},
{
"cell_type": "markdown",
- "id": "b821a3f6",
"metadata": {},
"source": [
- "#### The `0 `Subcomponent"
+ "\n",
+ "#### The `0 `Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "48280a2e",
"metadata": {},
"source": [
"If output is smaller than the indicated field width and you specify the digit zero (`0`) in ``, then values will be padded on the left with zeros instead of ASCII space characters:"
@@ -2265,8 +2170,7 @@
},
{
"cell_type": "code",
- "execution_count": 105,
- "id": "778ab5ed",
+ "execution_count": 72,
"metadata": {},
"outputs": [
{
@@ -2275,7 +2179,7 @@
"'00123'"
]
},
- "execution_count": 105,
+ "execution_count": 72,
"metadata": {},
"output_type": "execute_result"
}
@@ -2286,8 +2190,7 @@
},
{
"cell_type": "code",
- "execution_count": 98,
- "id": "5532d287",
+ "execution_count": 73,
"metadata": {},
"outputs": [
{
@@ -2296,7 +2199,7 @@
"'000012.3'"
]
},
- "execution_count": 98,
+ "execution_count": 73,
"metadata": {},
"output_type": "execute_result"
}
@@ -2307,7 +2210,6 @@
},
{
"cell_type": "markdown",
- "id": "badddddb",
"metadata": {},
"source": [
"You’ll typically use this for numeric values, as shown above. However, it works for string values as well:"
@@ -2316,7 +2218,6 @@
{
"cell_type": "code",
"execution_count": 106,
- "id": "07faf374",
"metadata": {},
"outputs": [
{
@@ -2336,7 +2237,6 @@
},
{
"cell_type": "markdown",
- "id": "7d65f8bb",
"metadata": {},
"source": [
"If you specify both `` and ``, then `` overrides `0`:"
@@ -2345,7 +2245,6 @@
{
"cell_type": "code",
"execution_count": 107,
- "id": "2af86e70",
"metadata": {},
"outputs": [
{
@@ -2365,7 +2264,6 @@
},
{
"cell_type": "markdown",
- "id": "5be90217",
"metadata": {},
"source": [
"`` and `0` essentially control the same thing, so there really isn’t any need to specify both. In fact, `0` is really superfluous, and was probably included as a convenience for developers who are familiar with the string modulo operator’s similar `0` conversion flag."
@@ -2373,15 +2271,15 @@
},
{
"cell_type": "markdown",
- "id": "165c23b4",
"metadata": {},
"source": [
- "#### The `` Subcomponent"
+ "`_subcomponent\">\n",
+ "#### The `` Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "109369f5",
"metadata": {},
"source": [
"`` specifies the minimum width of the output field:"
@@ -2389,8 +2287,7 @@
},
{
"cell_type": "code",
- "execution_count": 108,
- "id": "97afb5ad",
+ "execution_count": 76,
"metadata": {},
"outputs": [
{
@@ -2399,7 +2296,7 @@
"'foo '"
]
},
- "execution_count": 108,
+ "execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
@@ -2411,7 +2308,6 @@
{
"cell_type": "code",
"execution_count": 109,
- "id": "41cd3e37",
"metadata": {},
"outputs": [
{
@@ -2431,7 +2327,6 @@
},
{
"cell_type": "markdown",
- "id": "3ede251d",
"metadata": {},
"source": [
"Note that this is a **minimum field width**. Suppose you specify a value that’s longer than the minimum:"
@@ -2440,7 +2335,6 @@
{
"cell_type": "code",
"execution_count": 110,
- "id": "2075e60b",
"metadata": {},
"outputs": [
{
@@ -2460,7 +2354,6 @@
},
{
"cell_type": "markdown",
- "id": "8ccd11df",
"metadata": {},
"source": [
"In this case, `` is effectively ignored."
@@ -2468,15 +2361,15 @@
},
{
"cell_type": "markdown",
- "id": "4efd8e12",
"metadata": {},
"source": [
- "#### The `` Subcomponent"
+ "`_subcomponent\">\n",
+ "#### The `` Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "8eeffcea",
"metadata": {},
"source": [
"`` allows you to include a grouping separator character in numeric output. For decimal and floating-point presentation types, `` may be specified as either a comma character (`,`) or an underscore character (`_`). That character then separates each group of three digits in the output:"
@@ -2485,7 +2378,6 @@
{
"cell_type": "code",
"execution_count": 111,
- "id": "27cd1785",
"metadata": {},
"outputs": [
{
@@ -2506,7 +2398,6 @@
{
"cell_type": "code",
"execution_count": 112,
- "id": "14d78357",
"metadata": {},
"outputs": [
{
@@ -2527,7 +2418,6 @@
{
"cell_type": "code",
"execution_count": 113,
- "id": "9d6819f8",
"metadata": {},
"outputs": [
{
@@ -2548,7 +2438,6 @@
{
"cell_type": "code",
"execution_count": 114,
- "id": "3c305678",
"metadata": {},
"outputs": [
{
@@ -2568,7 +2457,6 @@
},
{
"cell_type": "markdown",
- "id": "0420ca47",
"metadata": {},
"source": [
"A `` value using an underscore (`_`) may also be specified with the binary, octal, and hexadecimal presentation types. In that case, each group of four digits is separated by an underscore character in the output:"
@@ -2577,7 +2465,6 @@
{
"cell_type": "code",
"execution_count": 115,
- "id": "e0be1a66",
"metadata": {},
"outputs": [
{
@@ -2598,7 +2485,6 @@
{
"cell_type": "code",
"execution_count": 116,
- "id": "74e9196f",
"metadata": {},
"outputs": [
{
@@ -2619,7 +2505,6 @@
{
"cell_type": "code",
"execution_count": 117,
- "id": "79f85fa1",
"metadata": {},
"outputs": [
{
@@ -2640,7 +2525,6 @@
{
"cell_type": "code",
"execution_count": 118,
- "id": "553db456",
"metadata": {},
"outputs": [
{
@@ -2660,7 +2544,6 @@
},
{
"cell_type": "markdown",
- "id": "ae4aec7c",
"metadata": {},
"source": [
"If you try to specify `` with any presentation type other than those listed above, then your code will raise an exception."
@@ -2668,15 +2551,15 @@
},
{
"cell_type": "markdown",
- "id": "a478d0e8",
"metadata": {},
"source": [
- "#### The `.` Subcomponent"
+ "`_subcomponent\">\n",
+ "#### The `.` Subcomponent [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "08789a90",
"metadata": {},
"source": [
"`.` specifies the number of digits after the decimal point for floating point presentation types:"
@@ -2684,8 +2567,7 @@
},
{
"cell_type": "code",
- "execution_count": 119,
- "id": "7a27b909",
+ "execution_count": 80,
"metadata": {},
"outputs": [
{
@@ -2694,7 +2576,7 @@
"' 1234.57'"
]
},
- "execution_count": 119,
+ "execution_count": 80,
"metadata": {},
"output_type": "execute_result"
}
@@ -2706,7 +2588,6 @@
{
"cell_type": "code",
"execution_count": 120,
- "id": "6a69f501",
"metadata": {},
"outputs": [
{
@@ -2727,7 +2608,6 @@
{
"cell_type": "code",
"execution_count": 121,
- "id": "312f0964",
"metadata": {},
"outputs": [
{
@@ -2748,7 +2628,6 @@
{
"cell_type": "code",
"execution_count": 122,
- "id": "70a2a6fa",
"metadata": {},
"outputs": [
{
@@ -2768,7 +2647,6 @@
},
{
"cell_type": "markdown",
- "id": "b19456d8",
"metadata": {},
"source": [
"For string types, `.` specifies the maximum width of the converted output:"
@@ -2777,7 +2655,6 @@
{
"cell_type": "code",
"execution_count": 123,
- "id": "9f0b1351",
"metadata": {},
"outputs": [
{
@@ -2797,7 +2674,6 @@
},
{
"cell_type": "markdown",
- "id": "008472f0",
"metadata": {},
"source": [
"If the output would be longer than the value specified, then it will be truncated."
@@ -2805,15 +2681,15 @@
},
{
"cell_type": "markdown",
- "id": "94b3b03e",
"metadata": {},
"source": [
- "## The Python Formatted String Literal (f-String)"
+ "\n",
+ "## The Python Formatted String Literal (f-String) [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "c849d1e1",
"metadata": {},
"source": [
"In version 3.6, a new Python string formatting syntax was introduced, called the **formatted string literal**. These are also informally called **f-strings**, a term that was initially coined in [PEP 498](https://www.python.org/dev/peps/pep-0498), where they were first proposed."
@@ -2821,15 +2697,15 @@
},
{
"cell_type": "markdown",
- "id": "ab96100b",
"metadata": {},
"source": [
- "### Why `str.format()` Isn’t Great"
+ "\n",
+ "### Why `str.format()` Isn’t Great [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "2be3f7a5",
"metadata": {},
"source": [
"Code using `str.format()` is much more easily readable than code using `%`-formatting, but `str.format()` can still be quite verbose when you are dealing with multiple parameters and longer strings. Take a look at this:"
@@ -2838,7 +2714,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "67a1bcee",
"metadata": {},
"outputs": [
{
@@ -2863,7 +2738,6 @@
},
{
"cell_type": "markdown",
- "id": "58f91908",
"metadata": {},
"source": [
"If you had the variables you wanted to pass to `.format()` in a dictionary, then you could just unpack it with `.format(**some_dict)` and reference the values by key in the string, but there has got to be a better way to do this."
@@ -2871,15 +2745,15 @@
},
{
"cell_type": "markdown",
- "id": "486f334c",
"metadata": {},
"source": [
- "### f-String Syntax"
+ "\n",
+ "### f-String Syntax [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "160b2ab9",
"metadata": {},
"source": [
"An f-string looks very much like a typical Python string except that it’s prepended by the character `f`:"
@@ -2887,8 +2761,7 @@
},
{
"cell_type": "code",
- "execution_count": 124,
- "id": "87875fd2",
+ "execution_count": 138,
"metadata": {},
"outputs": [
{
@@ -2897,7 +2770,7 @@
"'foo bar baz'"
]
},
- "execution_count": 124,
+ "execution_count": 138,
"metadata": {},
"output_type": "execute_result"
}
@@ -2908,7 +2781,6 @@
},
{
"cell_type": "markdown",
- "id": "93a59d44",
"metadata": {},
"source": [
"You can also use an uppercase `F`:"
@@ -2917,7 +2789,6 @@
{
"cell_type": "code",
"execution_count": 127,
- "id": "3e35be7d",
"metadata": {},
"outputs": [
{
@@ -2938,7 +2809,6 @@
{
"cell_type": "code",
"execution_count": 128,
- "id": "78b290c6",
"metadata": {},
"outputs": [
{
@@ -2959,7 +2829,6 @@
{
"cell_type": "code",
"execution_count": 129,
- "id": "e92d1784",
"metadata": {},
"outputs": [
{
@@ -2980,7 +2849,6 @@
{
"cell_type": "code",
"execution_count": 130,
- "id": "02370854",
"metadata": {},
"outputs": [
{
@@ -3000,7 +2868,6 @@
},
{
"cell_type": "markdown",
- "id": "4f1bfecb",
"metadata": {},
"source": [
"The magic of f-strings is that you can embed Python expressions directly inside them. Any portion of an f-string that’s enclosed in curly braces (`{}`) is treated as an **expression**. The expression is evaluated and converted to string representation, and the result is interpolated into the original string in that location:"
@@ -3009,7 +2876,6 @@
{
"cell_type": "code",
"execution_count": 133,
- "id": "3f89e5a1",
"metadata": {},
"outputs": [],
"source": [
@@ -3019,7 +2885,6 @@
{
"cell_type": "code",
"execution_count": 134,
- "id": "345e83f3",
"metadata": {},
"outputs": [
{
@@ -3036,7 +2901,6 @@
},
{
"cell_type": "markdown",
- "id": "0e928bf1",
"metadata": {},
"source": [
"The interpreter treats the remainder of the f-string—anything not inside curly braces—just as it would an ordinary string. For example, escape sequences are processed as expected:"
@@ -3045,7 +2909,6 @@
{
"cell_type": "code",
"execution_count": 135,
- "id": "3936a894",
"metadata": {},
"outputs": [],
"source": [
@@ -3055,7 +2918,6 @@
{
"cell_type": "code",
"execution_count": 136,
- "id": "f790bcdd",
"metadata": {},
"outputs": [
{
@@ -3074,7 +2936,6 @@
},
{
"cell_type": "markdown",
- "id": "1edce8c3",
"metadata": {},
"source": [
"Here’s the example from earlier using an f-string:"
@@ -3082,8 +2943,7 @@
},
{
"cell_type": "code",
- "execution_count": 137,
- "id": "61c7fab0",
+ "execution_count": 144,
"metadata": {},
"outputs": [],
"source": [
@@ -3094,8 +2954,7 @@
},
{
"cell_type": "code",
- "execution_count": 138,
- "id": "8e3ec234",
+ "execution_count": 145,
"metadata": {},
"outputs": [
{
@@ -3112,7 +2971,6 @@
},
{
"cell_type": "markdown",
- "id": "e87e98c9",
"metadata": {},
"source": [
"This is equivalent to the following:"
@@ -3121,7 +2979,6 @@
{
"cell_type": "code",
"execution_count": 139,
- "id": "c7834639",
"metadata": {},
"outputs": [],
"source": [
@@ -3133,7 +2990,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "afaa71d5",
"metadata": {},
"outputs": [
{
@@ -3150,7 +3006,6 @@
},
{
"cell_type": "markdown",
- "id": "3aad37a4",
"metadata": {},
"source": [
"Expressions embedded in f-strings can be almost arbitrarily complex. The examples below show some of the possibilities:"
@@ -3158,7 +3013,6 @@
},
{
"cell_type": "markdown",
- "id": "6b33892e",
"metadata": {},
"source": [
"- Variables"
@@ -3167,7 +3021,6 @@
{
"cell_type": "code",
"execution_count": 142,
- "id": "b015557b",
"metadata": {},
"outputs": [
{
@@ -3188,7 +3041,6 @@
},
{
"cell_type": "markdown",
- "id": "c746a191",
"metadata": {},
"source": [
"- Arithmetic expressions"
@@ -3197,7 +3049,6 @@
{
"cell_type": "code",
"execution_count": 143,
- "id": "4fb5d1f5",
"metadata": {},
"outputs": [
{
@@ -3216,7 +3067,6 @@
{
"cell_type": "code",
"execution_count": 144,
- "id": "ccc8a5e0",
"metadata": {},
"outputs": [
{
@@ -3234,7 +3084,6 @@
},
{
"cell_type": "markdown",
- "id": "37eefe23",
"metadata": {},
"source": [
"- Objects of composite types"
@@ -3243,7 +3092,6 @@
{
"cell_type": "code",
"execution_count": 145,
- "id": "03f895bb",
"metadata": {},
"outputs": [],
"source": [
@@ -3254,7 +3102,6 @@
{
"cell_type": "code",
"execution_count": 146,
- "id": "82771323",
"metadata": {},
"outputs": [
{
@@ -3271,7 +3118,6 @@
},
{
"cell_type": "markdown",
- "id": "28c94f15",
"metadata": {},
"source": [
"- Indexing, slicing, and key references"
@@ -3280,7 +3126,6 @@
{
"cell_type": "code",
"execution_count": 147,
- "id": "9d9f900d",
"metadata": {},
"outputs": [],
"source": [
@@ -3291,7 +3136,6 @@
{
"cell_type": "code",
"execution_count": 148,
- "id": "37a712c4",
"metadata": {},
"outputs": [
{
@@ -3309,7 +3153,6 @@
{
"cell_type": "code",
"execution_count": 149,
- "id": "7f6b7b05",
"metadata": {},
"outputs": [
{
@@ -3327,7 +3170,6 @@
{
"cell_type": "code",
"execution_count": 150,
- "id": "4f802838",
"metadata": {},
"outputs": [
{
@@ -3345,7 +3187,6 @@
{
"cell_type": "code",
"execution_count": 151,
- "id": "010503e9",
"metadata": {},
"outputs": [
{
@@ -3362,7 +3203,6 @@
},
{
"cell_type": "markdown",
- "id": "dc32a2ac",
"metadata": {},
"source": [
"- Function and method calls"
@@ -3371,7 +3211,6 @@
{
"cell_type": "code",
"execution_count": 152,
- "id": "99d9e07e",
"metadata": {},
"outputs": [
{
@@ -3390,7 +3229,6 @@
{
"cell_type": "code",
"execution_count": 153,
- "id": "0372e5cb",
"metadata": {},
"outputs": [
{
@@ -3412,7 +3250,6 @@
{
"cell_type": "code",
"execution_count": 154,
- "id": "c08561ed",
"metadata": {},
"outputs": [
{
@@ -3430,7 +3267,6 @@
},
{
"cell_type": "markdown",
- "id": "12de166d",
"metadata": {},
"source": [
"- Conditional expressions"
@@ -3439,7 +3275,6 @@
{
"cell_type": "code",
"execution_count": 155,
- "id": "0c0546fe",
"metadata": {},
"outputs": [
{
@@ -3459,7 +3294,6 @@
{
"cell_type": "code",
"execution_count": 156,
- "id": "51b1c8c9",
"metadata": {},
"outputs": [
{
@@ -3480,7 +3314,6 @@
},
{
"cell_type": "markdown",
- "id": "268c19ca",
"metadata": {},
"source": [
"- Object attributes"
@@ -3489,7 +3322,6 @@
{
"cell_type": "code",
"execution_count": 157,
- "id": "2ad1893c",
"metadata": {},
"outputs": [
{
@@ -3511,7 +3343,6 @@
{
"cell_type": "code",
"execution_count": 158,
- "id": "5b8102af",
"metadata": {},
"outputs": [
{
@@ -3528,7 +3359,6 @@
},
{
"cell_type": "markdown",
- "id": "b6b75965",
"metadata": {},
"source": [
"To include a literal curly brace in an f-string, escape it by doubling it, the same as you would in a template string for Python’s `.format()` method:"
@@ -3537,7 +3367,6 @@
{
"cell_type": "code",
"execution_count": 159,
- "id": "d0468ad7",
"metadata": {},
"outputs": [
{
@@ -3558,7 +3387,6 @@
},
{
"cell_type": "markdown",
- "id": "bc5fad51",
"metadata": {},
"source": [
"You may prefix an f-string with `'r'` or `'R'` to indicate that it is a raw f-string. In that case, backslash sequences are left intact, just like with an ordinary string:"
@@ -3567,7 +3395,6 @@
{
"cell_type": "code",
"execution_count": 160,
- "id": "e5accc78",
"metadata": {},
"outputs": [],
"source": [
@@ -3577,7 +3404,6 @@
{
"cell_type": "code",
"execution_count": 161,
- "id": "72a78782",
"metadata": {},
"outputs": [
{
@@ -3597,7 +3423,6 @@
{
"cell_type": "code",
"execution_count": 162,
- "id": "0bdc4212",
"metadata": {},
"outputs": [
{
@@ -3615,7 +3440,6 @@
{
"cell_type": "code",
"execution_count": 163,
- "id": "895f06c3",
"metadata": {},
"outputs": [
{
@@ -3632,7 +3456,6 @@
},
{
"cell_type": "markdown",
- "id": "0fb0503d",
"metadata": {},
"source": [
"Note that you can specify the `'r'` first and then the `'f'`, or vice-versa."
@@ -3640,15 +3463,15 @@
},
{
"cell_type": "markdown",
- "id": "a3f57920",
"metadata": {},
"source": [
- "### f-String Expression Limitations"
+ "\n",
+ "### f-String Expression Limitations [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "a643e956",
"metadata": {},
"source": [
"There are a few minor **restrictions** on f-string expression. These aren’t too limiting, but you should know what they are. First, an f-string expression **can’t be empty**:"
@@ -3656,16 +3479,15 @@
},
{
"cell_type": "code",
- "execution_count": 164,
- "id": "ab38df62",
+ "execution_count": 150,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
- "evalue": "f-string: empty expression not allowed (, line 1)",
+ "evalue": "f-string: empty expression not allowed (, line 1)",
"output_type": "error",
"traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m f'foo{}bar'\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m f-string: empty expression not allowed\n"
+ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m f'foo{}bar'\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m f-string: empty expression not allowed\n"
]
}
],
@@ -3675,7 +3497,6 @@
},
{
"cell_type": "markdown",
- "id": "e630873b",
"metadata": {},
"source": [
"It isn’t obvious why you’d want to do this. But if you feel compelled to try, then just know that it won’t work."
@@ -3683,7 +3504,6 @@
},
{
"cell_type": "markdown",
- "id": "c305964b",
"metadata": {},
"source": [
"Additionally, an f-string expression **can’t contain a backslash (`\\`) character**. Among other things, that means you can’t use a backslash escape sequence in an f-string expression:"
@@ -3691,27 +3511,25 @@
},
{
"cell_type": "code",
- "execution_count": 165,
- "id": "24bccb82",
+ "execution_count": 161,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
- "evalue": "f-string expression part cannot include a backslash (, line 1)",
+ "evalue": "f-string expression part cannot include a backslash (, line 1)",
"output_type": "error",
"traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(f'foo{\\n}bar')\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m f-string expression part cannot include a backslash\n"
+ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(f'foo{\"\\n\"}bar \\\\')\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m f-string expression part cannot include a backslash\n"
]
}
],
"source": [
- "print(f'foo{\\n}bar')"
+ "print(f'foo{\"\\n\"}bar \\\\')"
]
},
{
"cell_type": "code",
"execution_count": 166,
- "id": "5410f830",
"metadata": {},
"outputs": [
{
@@ -3729,7 +3547,6 @@
},
{
"cell_type": "markdown",
- "id": "41dc209e",
"metadata": {},
"source": [
"You can get around this limitation by creating a temporary variable that contains the escape sequence you want to insert:"
@@ -3738,7 +3555,6 @@
{
"cell_type": "code",
"execution_count": 167,
- "id": "f98d9684",
"metadata": {},
"outputs": [
{
@@ -3758,7 +3574,6 @@
{
"cell_type": "code",
"execution_count": 168,
- "id": "0f088775",
"metadata": {},
"outputs": [
{
@@ -3776,7 +3591,6 @@
},
{
"cell_type": "markdown",
- "id": "26ba4bf4",
"metadata": {},
"source": [
"Lastly, an expression in an f-string that is triple-quoted **can’t contain comments**:"
@@ -3785,7 +3599,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "a4673404",
"metadata": {},
"outputs": [],
"source": [
@@ -3795,7 +3608,6 @@
{
"cell_type": "code",
"execution_count": 169,
- "id": "6f8645b8",
"metadata": {},
"outputs": [
{
@@ -3815,7 +3627,6 @@
{
"cell_type": "code",
"execution_count": 170,
- "id": "88423a41",
"metadata": {},
"outputs": [
{
@@ -3835,7 +3646,6 @@
},
{
"cell_type": "markdown",
- "id": "cab8c26a",
"metadata": {},
"source": [
"Note, however, that the multiline f-string may contain embedded newlines."
@@ -3843,15 +3653,15 @@
},
{
"cell_type": "markdown",
- "id": "c2c6f65b",
"metadata": {},
"source": [
- "### f-String Formatting"
+ "\n",
+ "### f-String Formatting [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "ab8e156e",
"metadata": {},
"source": [
"Like Python’s `.format()` method, f-strings support extensive **modifiers** that control the final appearance of the output string. There’s more good news, too. If you’ve mastered the Python `.format()` method, then **you already know how to use Python to format f-strings!**"
@@ -3859,7 +3669,6 @@
},
{
"cell_type": "markdown",
- "id": "4c741539",
"metadata": {},
"source": [
"Expressions in f-strings can be modified by a `` or ``, just like replacement fields used in the `.format()` template. The syntax is identical. In fact, in both cases Python will format the replacement field using the same internal function. In the following example, `!r` is specified as a `` component in the `.format()` template string:"
@@ -3868,7 +3677,6 @@
{
"cell_type": "code",
"execution_count": 172,
- "id": "1998c68a",
"metadata": {},
"outputs": [
{
@@ -3889,7 +3697,6 @@
},
{
"cell_type": "markdown",
- "id": "dd03a1d3",
"metadata": {},
"source": [
"This forces conversion to be performed by repr(). You can get essentially the same code using an f-string instead:"
@@ -3898,7 +3705,6 @@
{
"cell_type": "code",
"execution_count": 173,
- "id": "f6e9981f",
"metadata": {},
"outputs": [
{
@@ -3919,7 +3725,6 @@
},
{
"cell_type": "markdown",
- "id": "9e89e11f",
"metadata": {},
"source": [
"All the `` components that work with `.format()` also work with f-strings:"
@@ -3928,7 +3733,6 @@
{
"cell_type": "code",
"execution_count": 174,
- "id": "3142bb8f",
"metadata": {},
"outputs": [
{
@@ -3950,7 +3754,6 @@
{
"cell_type": "code",
"execution_count": 175,
- "id": "f3af17fc",
"metadata": {},
"outputs": [
{
@@ -3971,7 +3774,6 @@
{
"cell_type": "code",
"execution_count": 176,
- "id": "32f85311",
"metadata": {},
"outputs": [
{
@@ -3993,7 +3795,6 @@
{
"cell_type": "code",
"execution_count": 177,
- "id": "4bfe06ad",
"metadata": {},
"outputs": [
{
@@ -4014,7 +3815,6 @@
{
"cell_type": "code",
"execution_count": 178,
- "id": "f3a017cc",
"metadata": {},
"outputs": [
{
@@ -4036,7 +3836,6 @@
{
"cell_type": "code",
"execution_count": 179,
- "id": "c6d4fbd0",
"metadata": {},
"outputs": [
{
@@ -4056,7 +3855,6 @@
},
{
"cell_type": "markdown",
- "id": "3129b50c",
"metadata": {},
"source": [
"f-strings and Python’s `.format()` method are, more or less, two different ways of doing the same thing, with f-strings being a more concise shorthand. The following expressions are essentially the same:\n",
@@ -4070,16 +3868,17 @@
},
{
"cell_type": "markdown",
- "id": "b2d49635",
"metadata": {},
"source": [
- "## Python f-string benchmarks\n",
+ "\n",
+ "## Python f-string benchmarks [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"Python 3.6 f-strings have been shown to be the fastest string formatting method in microbenchmarks by Python core dev [Raymond Hettinger](https://twitter.com/raymondh):"
]
},
{
"cell_type": "markdown",
- "id": "6b4bb74f",
"metadata": {},
"source": [
"
"
@@ -4087,8 +3886,7 @@
},
{
"cell_type": "code",
- "execution_count": 179,
- "id": "05c0682a",
+ "execution_count": 162,
"metadata": {},
"outputs": [],
"source": [
@@ -4098,15 +3896,14 @@
},
{
"cell_type": "code",
- "execution_count": 180,
- "id": "499b031c",
+ "execution_count": 164,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "79.7 ns ± 3.94 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
+ "138 ns ± 2.35 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
]
}
],
@@ -4117,15 +3914,14 @@
},
{
"cell_type": "code",
- "execution_count": 181,
- "id": "6c2c562a",
+ "execution_count": 165,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "108 ns ± 6.69 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
+ "161 ns ± 0.943 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
]
}
],
@@ -4136,15 +3932,14 @@
},
{
"cell_type": "code",
- "execution_count": 182,
- "id": "b915dc7c",
+ "execution_count": 169,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "113 ns ± 2.52 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
+ "176 ns ± 5.79 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
]
}
],
@@ -4155,15 +3950,14 @@
},
{
"cell_type": "code",
- "execution_count": 183,
- "id": "0d279f67",
+ "execution_count": 170,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "133 ns ± 7.61 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
+ "233 ns ± 4.31 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
]
}
],
@@ -4174,15 +3968,14 @@
},
{
"cell_type": "code",
- "execution_count": 184,
- "id": "72c9a8bd",
+ "execution_count": 171,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "179 ns ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n"
+ "322 ns ± 1.37 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
]
}
],
@@ -4193,8 +3986,7 @@
},
{
"cell_type": "code",
- "execution_count": 185,
- "id": "424db3d0",
+ "execution_count": 172,
"metadata": {},
"outputs": [],
"source": [
@@ -4203,15 +3995,14 @@
},
{
"cell_type": "code",
- "execution_count": 186,
- "id": "088d2322",
+ "execution_count": 173,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "1.89 µs ± 104 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
+ "2.83 µs ± 34.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
]
}
],
@@ -4222,7 +4013,6 @@
},
{
"cell_type": "markdown",
- "id": "7e7335ed",
"metadata": {},
"source": [
"In relative speed factors, that’s:\n",
@@ -4237,7 +4027,6 @@
},
{
"cell_type": "markdown",
- "id": "68c2f84c",
"metadata": {},
"source": [
"The reason for this speedy performance was described by Python core dev [Serhiy Storchaka](https://twitter.com/SerhiyStorchaka):"
@@ -4245,7 +4034,6 @@
},
{
"cell_type": "markdown",
- "id": "af6ea2f1",
"metadata": {},
"source": [
"
"
@@ -4253,15 +4041,15 @@
},
{
"cell_type": "markdown",
- "id": "305bb774",
"metadata": {},
"source": [
- "## Conclusion"
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "4788325c",
"metadata": {},
"source": [
"In this tutorial, you mastered two additional techniques that you can use in Python to format string data. You should now have all the tools you need to prepare string data for output or display!"
@@ -4269,7 +4057,6 @@
},
{
"cell_type": "markdown",
- "id": "322c2e0b",
"metadata": {},
"source": [
"You might be wondering which Python formatting technique you should use. Under what circumstances would you choose `.format()` over the **f-string**? See Python String Formatting Best Practices for some considerations to take into account."
@@ -4277,7 +4064,6 @@
},
{
"cell_type": "markdown",
- "id": "9faa0b99",
"metadata": {},
"source": [
"In the next section, you’re going to learn more about **functions** in Python. Throughout this tutorial series, you’ve seen many examples of Python’s **built-in functions**. In Python, as in most programming languages, you can define your own custom **user-defined functions** as well. If you can’t wait to learn how then continue on to the next section!"
@@ -4285,16 +4071,17 @@
},
{
"cell_type": "markdown",
- "id": "894ad1cc",
"metadata": {},
"source": [
- "## Further Reading\n",
+ "\n",
+ "## Further Reading [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"If you’d like to read an extended discussion about string interpolation, take a look at [PEP 502](https://www.python.org/dev/peps/pep-0502/). Also, the [PEP 536](https://www.python.org/dev/peps/pep-0536/) draft has some more thoughts about the future of f-strings."
]
},
{
"cell_type": "markdown",
- "id": "bdcd7e73",
"metadata": {},
"source": [
"For more fun with strings, check out the following articles:\n",
@@ -4320,7 +4107,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/17 Defining Your Own Python Function.ipynb b/Python/01. Basics/18 Defining Your Own Python Function.ipynb
similarity index 90%
rename from python/01. Basics/17 Defining Your Own Python Function.ipynb
rename to Python/01. Basics/18 Defining Your Own Python Function.ipynb
index 6571902..ef3e994 100755
--- a/python/01. Basics/17 Defining Your Own Python Function.ipynb
+++ b/Python/01. Basics/18 Defining Your Own Python Function.ipynb
@@ -4,7 +4,56 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Defining Your Own Python Function"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Defining Your Own Python Function \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Functions in Python](#functions_in_python)\n",
+ "* [The Importance of Python Functions](#the_importance_of_python_functions)\n",
+ " * [Abstraction and Reusability](#abstraction_and_reusability)\n",
+ " * [Modularity](#modularity)\n",
+ " * [Namespace Separation](#namespace_separation)\n",
+ "* [Function Calls and Definition](#function_calls_and_definition)\n",
+ "* [Argument Passing](#argument_passing)\n",
+ " * [Positional Arguments](#positional_arguments)\n",
+ " * [Keyword Arguments](#keyword_arguments)\n",
+ " * [Default Parameters](#default_parameters)\n",
+ "* [Mutable Default Parameter Values](#mutable_default_parameter_values)\n",
+ "* [Pass-By-Value vs Pass-By-Reference](#pass-by-value_vs_pass-by-reference)\n",
+ " * [Argument Passing Summary](#argument_passing_summary)\n",
+ " * [Side Effects](#side_effects)\n",
+ "* [The return Statement](#the_return_statement)\n",
+ " * [Exiting a Function](#exiting_a_function)\n",
+ " * [Returning Data to the Caller](#returning_data_to_the_caller)\n",
+ " * [Revisiting Side Effects](#revisiting_side_effects)\n",
+ "* [Variable-Length Argument Lists](#variable-length_argument_lists)\n",
+ " * [Argument Tuple Packing](#argument_tuple_packing)\n",
+ " * [Argument Tuple Unpacking](#argument_tuple_unpacking)\n",
+ " * [Argument Dictionary Packing](#argument_dictionary_packing)\n",
+ " * [Argument Dictionary Unpacking](#argument_dictionary_unpacking)\n",
+ " * [Putting It All Together](#putting_it_all_together)\n",
+ " * [Multiple Unpackings in a Python Function Call](#multiple_unpackings_in_a_python_function_call)\n",
+ "* [Keyword-Only Arguments](#keyword-only_arguments)\n",
+ "* [Positional-Only Arguments](#positional-only_arguments)\n",
+ "* [Docstrings](#docstrings)\n",
+ "* [Python Function Annotations](#python_function_annotations)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
@@ -30,7 +79,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Functions in Python"
+ "\n",
+ "## Functions in Python [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -60,7 +111,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -69,16 +120,16 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "140291555089264"
+ "140587838501936"
]
},
- "execution_count": 9,
+ "execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
@@ -96,7 +147,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 5,
"metadata": {},
"outputs": [
{
@@ -105,7 +156,7 @@
"4"
]
},
- "execution_count": 3,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@@ -124,7 +175,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
@@ -133,7 +184,7 @@
"False"
]
},
- "execution_count": 4,
+ "execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
@@ -144,7 +195,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 11,
"metadata": {},
"outputs": [
{
@@ -153,7 +204,7 @@
"True"
]
},
- "execution_count": 5,
+ "execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
@@ -164,7 +215,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 12,
"metadata": {},
"outputs": [
{
@@ -173,7 +224,7 @@
"False"
]
},
- "execution_count": 7,
+ "execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@@ -184,7 +235,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
@@ -193,7 +244,7 @@
"True"
]
},
- "execution_count": 8,
+ "execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@@ -244,7 +295,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The Importance of Python Functions"
+ "\n",
+ "## The Importance of Python Functions [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -270,7 +323,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Abstraction and Reusability"
+ "\n",
+ "### Abstraction and Reusability [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -312,7 +367,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Modularity"
+ "\n",
+ "### Modularity [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -426,7 +483,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Namespace Separation"
+ "\n",
+ "### Namespace Separation [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -468,7 +527,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Function Calls and Definition"
+ "\n",
+ "## Function Calls and Definition [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -637,7 +698,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Argument Passing"
+ "\n",
+ "## Argument Passing [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -647,11 +710,55 @@
"So far in this tutorial, the functions you’ve defined haven’t taken any arguments. That can sometimes be useful, and you’ll occasionally write such functions. More often, though, you’ll want to **pass data into a function** so that its behavior can vary from one invocation to the next. Let’s see how to do that."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# formal parameters\n",
+ "# function definition\n",
+ "def concat(a, b, separator):\n",
+ " print(f\"{a}{separator}{b}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 60,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ali ____ hejazi\n"
+ ]
+ }
+ ],
+ "source": [
+ "# actual parameters\n",
+ "# function call\n",
+ "concat('ali', 'hejazi', ' ____ ')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "0 a 'ali'\n",
+ "1 b 'hejazi'\n",
+ "2 separator ' ___ '"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Positional Arguments"
+ "\n",
+ "### Positional Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -663,7 +770,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 46,
"metadata": {},
"outputs": [],
"source": [
@@ -680,7 +787,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 53,
"metadata": {},
"outputs": [
{
@@ -692,7 +799,7 @@
}
],
"source": [
- "f(6, 'bananas', 1.74)"
+ "f(6, 'bananas', x)"
]
},
{
@@ -736,7 +843,7 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 56,
"metadata": {},
"outputs": [
{
@@ -827,7 +934,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Keyword Arguments"
+ "\n",
+ "### Keyword Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -839,7 +948,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 77,
"metadata": {},
"outputs": [
{
@@ -863,23 +972,19 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 79,
"metadata": {},
"outputs": [
{
- "ename": "TypeError",
- "evalue": "f() got an unexpected keyword argument 'cost'",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqty\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'bananas'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcost\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1.74\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mTypeError\u001b[0m: f() got an unexpected keyword argument 'cost'"
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "6 bananas cost $1.74\n"
]
}
],
"source": [
- "f(qty=6, item='bananas', cost=1.74)"
+ "f(qty=6, item='bananas', price=1.74)"
]
},
{
@@ -915,7 +1020,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 80,
"metadata": {},
"outputs": [
{
@@ -925,7 +1030,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Still too few arguments\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqty\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'bananas'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_42366/2759971996.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Still too few arguments\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mqty\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'bananas'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: f() missing 1 required positional argument: 'price'"
]
}
@@ -992,15 +1097,15 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 81,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
- "evalue": "positional argument follows keyword argument (, line 1)",
+ "evalue": "positional argument follows keyword argument (185035404.py, line 1)",
"output_type": "error",
"traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m f(6, item='bananas', 1.74)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n"
+ "\u001b[0;36m File \u001b[0;32m\"/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_42366/185035404.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m f(6, item='bananas', 1.74)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n"
]
}
],
@@ -1019,7 +1124,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Default Parameters"
+ "\n",
+ "### Default Parameters [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1031,18 +1138,9 @@
},
{
"cell_type": "code",
- "execution_count": 166,
+ "execution_count": 100,
"metadata": {},
- "outputs": [
- {
- "ename": "SyntaxError",
- "evalue": "non-default argument follows default argument (, line 1)",
- "output_type": "error",
- "traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m def f(qty=6, item, price=1.74):\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m non-default argument follows default argument\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"def f(qty=6, item='bananas', price=1.74):\n",
" print(f'{qty} {item} cost ${price:.2f}')"
@@ -1057,7 +1155,7 @@
},
{
"cell_type": "code",
- "execution_count": 159,
+ "execution_count": 101,
"metadata": {},
"outputs": [
{
@@ -1074,7 +1172,7 @@
},
{
"cell_type": "code",
- "execution_count": 160,
+ "execution_count": 102,
"metadata": {},
"outputs": [
{
@@ -1091,7 +1189,7 @@
},
{
"cell_type": "code",
- "execution_count": 161,
+ "execution_count": 103,
"metadata": {},
"outputs": [
{
@@ -1108,7 +1206,7 @@
},
{
"cell_type": "code",
- "execution_count": 162,
+ "execution_count": 104,
"metadata": {},
"outputs": [
{
@@ -1125,7 +1223,7 @@
},
{
"cell_type": "code",
- "execution_count": 163,
+ "execution_count": 105,
"metadata": {},
"outputs": [
{
@@ -1142,7 +1240,7 @@
},
{
"cell_type": "code",
- "execution_count": 164,
+ "execution_count": 106,
"metadata": {},
"outputs": [
{
@@ -1172,7 +1270,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Mutable Default Parameter Values"
+ "\n",
+ "## Mutable Default Parameter Values [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1318,7 +1418,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Oops! You might have expected each subsequent call to also return the singleton list `['###']`, just like the first. Instead, the return value keeps growing. What happened?"
+ "Oops! You might have expected each subsequent call to also return the singleton list `['###']`, just like the first. Instead, the return value keeps growing. What happened? "
]
},
{
@@ -1451,7 +1551,7 @@
"def f(my_list=None):\n",
" if my_list is None:\n",
" my_list = []\n",
- " my_list.append('###')\n",
+ " my_list.append('###') \n",
" return my_list"
]
},
@@ -1566,7 +1666,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Pass-By-Value vs Pass-By-Reference"
+ "\n",
+ "## Pass-By-Value vs Pass-By-Reference [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1705,7 +1807,7 @@
},
{
"cell_type": "code",
- "execution_count": 53,
+ "execution_count": 187,
"metadata": {},
"outputs": [],
"source": [
@@ -1717,14 +1819,14 @@
},
{
"cell_type": "code",
- "execution_count": 54,
+ "execution_count": 188,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "x = 5 / id(x) = 94054518445120\n"
+ "x = 5 / id(x) = 4495721200\n"
]
}
],
@@ -1735,15 +1837,15 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": 189,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "fx = 5 / id(fx) = 94054518445120\n",
- "fx = 10 / id(fx) = 94054518445280\n"
+ "fx = 5 / id(fx) = 4495721200\n",
+ "fx = 10 / id(fx) = 4495721360\n"
]
}
],
@@ -1753,14 +1855,14 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 190,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "x = 5 / id(x) = 94054518445120\n"
+ "x = 5 / id(x) = 4495721200\n"
]
}
],
@@ -1851,7 +1953,7 @@
},
{
"cell_type": "code",
- "execution_count": 66,
+ "execution_count": 219,
"metadata": {},
"outputs": [],
"source": [
@@ -1861,7 +1963,7 @@
},
{
"cell_type": "code",
- "execution_count": 67,
+ "execution_count": 220,
"metadata": {},
"outputs": [],
"source": [
@@ -1870,7 +1972,7 @@
},
{
"cell_type": "code",
- "execution_count": 68,
+ "execution_count": 221,
"metadata": {},
"outputs": [],
"source": [
@@ -1879,7 +1981,7 @@
},
{
"cell_type": "code",
- "execution_count": 69,
+ "execution_count": 222,
"metadata": {},
"outputs": [
{
@@ -1888,7 +1990,7 @@
"['---', 'bar', 'baz', 'qux']"
]
},
- "execution_count": 69,
+ "execution_count": 222,
"metadata": {},
"output_type": "execute_result"
}
@@ -1913,7 +2015,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 202,
"metadata": {},
"outputs": [],
"source": [
@@ -1923,7 +2025,7 @@
},
{
"cell_type": "code",
- "execution_count": 47,
+ "execution_count": 203,
"metadata": {},
"outputs": [],
"source": [
@@ -1932,7 +2034,7 @@
},
{
"cell_type": "code",
- "execution_count": 48,
+ "execution_count": 204,
"metadata": {},
"outputs": [],
"source": [
@@ -1941,7 +2043,7 @@
},
{
"cell_type": "code",
- "execution_count": 49,
+ "execution_count": 205,
"metadata": {},
"outputs": [
{
@@ -1950,7 +2052,7 @@
"{'foo': 1, 'bar': 22, 'baz': 3}"
]
},
- "execution_count": 49,
+ "execution_count": 205,
"metadata": {},
"output_type": "execute_result"
}
@@ -1970,7 +2072,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Argument Passing Summary"
+ "\n",
+ "### Argument Passing Summary [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -1991,7 +2095,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Side Effects"
+ "\n",
+ "### Side Effects [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -2028,7 +2134,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## The return Statement"
+ "\n",
+ "## The return Statement [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -2052,7 +2160,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Exiting a Function"
+ "\n",
+ "### Exiting a Function [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -2108,28 +2218,35 @@
},
{
"cell_type": "code",
- "execution_count": 77,
+ "execution_count": 49,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def f(x):\n",
+ " if x < 0:\n",
+ " return \"x is less than zero\"\n",
+ " if x > 100:\n",
+ " return \"x is greater than 100\"\n",
+ " \n",
+ " return x"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "None\n",
- "None\n",
- "64\n",
- "None\n"
+ "x is less than zero\n",
+ "x is greater than 100\n",
+ "64\n"
]
}
],
"source": [
- "def f(x):\n",
- " if x < 0:\n",
- " return\n",
- " if x > 100:\n",
- " return\n",
- " print(x)\n",
- " \n",
"print(f(-3))\n",
"print(f(105))\n",
"print(f(64))"
@@ -2172,7 +2289,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Returning Data to the Caller"
+ "\n",
+ "### Returning Data to the Caller [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -2601,7 +2720,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Revisiting Side Effects"
+ "\n",
+ "### Revisiting Side Effects [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -2704,7 +2825,7 @@
},
{
"cell_type": "code",
- "execution_count": 106,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -2714,7 +2835,7 @@
},
{
"cell_type": "code",
- "execution_count": 107,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -2723,7 +2844,7 @@
},
{
"cell_type": "code",
- "execution_count": 108,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -2732,7 +2853,7 @@
},
{
"cell_type": "code",
- "execution_count": 109,
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
@@ -2741,7 +2862,7 @@
"10"
]
},
- "execution_count": 109,
+ "execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@@ -2898,7 +3019,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Variable-Length Argument Lists"
+ "\n",
+ "## Variable-Length Argument Lists [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -2910,7 +3033,7 @@
},
{
"cell_type": "code",
- "execution_count": 174,
+ "execution_count": 300,
"metadata": {},
"outputs": [],
"source": [
@@ -2927,7 +3050,7 @@
},
{
"cell_type": "code",
- "execution_count": 175,
+ "execution_count": 301,
"metadata": {},
"outputs": [
{
@@ -2936,7 +3059,7 @@
"2.0"
]
},
- "execution_count": 175,
+ "execution_count": 301,
"metadata": {},
"output_type": "execute_result"
}
@@ -2954,7 +3077,7 @@
},
{
"cell_type": "code",
- "execution_count": 120,
+ "execution_count": 302,
"metadata": {},
"outputs": [
{
@@ -2964,7 +3087,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mavg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_42366/1612909575.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mavg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: avg() takes 3 positional arguments but 4 were given"
]
}
@@ -3029,20 +3152,20 @@
},
{
"cell_type": "code",
- "execution_count": 128,
+ "execution_count": 303,
"metadata": {},
"outputs": [],
"source": [
"def avg(a):\n",
" total = 0\n",
" for v in a:\n",
- " total += v\n",
+ " total += v\n",
" return total / len(a)"
]
},
{
"cell_type": "code",
- "execution_count": 129,
+ "execution_count": 304,
"metadata": {},
"outputs": [
{
@@ -3051,7 +3174,7 @@
"2.0"
]
},
- "execution_count": 129,
+ "execution_count": 304,
"metadata": {},
"output_type": "execute_result"
}
@@ -3062,7 +3185,7 @@
},
{
"cell_type": "code",
- "execution_count": 130,
+ "execution_count": 305,
"metadata": {},
"outputs": [
{
@@ -3071,7 +3194,7 @@
"3.0"
]
},
- "execution_count": 130,
+ "execution_count": 305,
"metadata": {},
"output_type": "execute_result"
}
@@ -3127,7 +3250,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Argument Tuple Packing"
+ "\n",
+ "### Argument Tuple Packing [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -3365,7 +3490,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Argument Tuple Unpacking"
+ "\n",
+ "### Argument Tuple Unpacking [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -3574,7 +3701,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Argument Dictionary Packing"
+ "\n",
+ "### Argument Dictionary Packing [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -3629,7 +3758,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Argument Dictionary Unpacking"
+ "\n",
+ "### Argument Dictionary Unpacking [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -3742,7 +3873,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Putting It All Together"
+ "\n",
+ "### Putting It All Together [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -3761,7 +3894,7 @@
},
{
"cell_type": "code",
- "execution_count": 162,
+ "execution_count": 61,
"metadata": {},
"outputs": [],
"source": [
@@ -3774,7 +3907,7 @@
},
{
"cell_type": "code",
- "execution_count": 163,
+ "execution_count": 62,
"metadata": {},
"outputs": [
{
@@ -3803,7 +3936,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Multiple Unpackings in a Python Function Call"
+ "\n",
+ "### Multiple Unpackings in a Python Function Call [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -3821,7 +3956,7 @@
"source": [
"def f(*args):\n",
" for i in args:\n",
- " print(i)"
+ " print(i)"
]
},
{
@@ -3997,7 +4132,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Keyword-Only Arguments"
+ "\n",
+ "## Keyword-Only Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -4813,7 +4950,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Positional-Only Arguments"
+ "\n",
+ "## Positional-Only Arguments [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -4925,18 +5064,9 @@
},
{
"cell_type": "code",
- "execution_count": 300,
+ "execution_count": 48,
"metadata": {},
- "outputs": [
- {
- "ename": "SyntaxError",
- "evalue": "invalid syntax (, line 2)",
- "output_type": "error",
- "traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m def f(x, y, /, z, w, *, a, b):\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"# This is Python 3.8\n",
"def f(x, y, /, z, w, *, a, b):\n",
@@ -4945,18 +5075,14 @@
},
{
"cell_type": "code",
- "execution_count": 301,
+ "execution_count": 11,
"metadata": {},
"outputs": [
{
- "ename": "TypeError",
- "evalue": "f() takes 0 positional arguments but 2 were given",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mz\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mTypeError\u001b[0m: f() takes 0 positional arguments but 2 were given"
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1 2 3 4 5 6\n"
]
}
],
@@ -4966,18 +5092,14 @@
},
{
"cell_type": "code",
- "execution_count": 302,
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
- "ename": "TypeError",
- "evalue": "f() takes 0 positional arguments but 3 were given",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mw\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;31mTypeError\u001b[0m: f() takes 0 positional arguments but 3 were given"
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1 2 3 4 5 6\n"
]
}
],
@@ -5000,7 +5122,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Docstrings"
+ "\n",
+ "## Docstrings [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -5189,7 +5313,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Python Function Annotations"
+ "\n",
+ "## Python Function Annotations [
](#table_of_contents)\n",
+ "\n"
]
},
{
@@ -5199,6 +5325,23 @@
"As of version 3.0, Python provides an additional feature for documenting a function called a **function annotation**. Annotations provide a way to attach metadata to a function’s parameters and return value."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def f(a: float, b: float = 8) -> int:\n",
+ " \"\"\"Add two number.\n",
+ "\n",
+ " :param a: First input\n",
+ " :param b: Second input\n",
+ " :return: Sum of two numbers.\n",
+ " \"\"\"\n",
+ " \n",
+ " return a + b"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -5227,16 +5370,16 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 71,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "{'a': '', 'b': '', 'return': ''}"
+ "{'a': int, 'b': int, 'return': int}"
]
},
- "execution_count": 18,
+ "execution_count": 71,
"metadata": {},
"output_type": "execute_result"
}
@@ -5254,16 +5397,16 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 72,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "''"
+ "int"
]
},
- "execution_count": 19,
+ "execution_count": 72,
"metadata": {},
"output_type": "execute_result"
}
@@ -5274,16 +5417,16 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 73,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "''"
+ "int"
]
},
- "execution_count": 20,
+ "execution_count": 73,
"metadata": {},
"output_type": "execute_result"
}
@@ -5294,16 +5437,16 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 74,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "''"
+ "int"
]
},
- "execution_count": 21,
+ "execution_count": 74,
"metadata": {},
"output_type": "execute_result"
}
@@ -5386,7 +5529,7 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 82,
"metadata": {},
"outputs": [],
"source": [
@@ -5650,7 +5793,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"As applications grow larger, it becomes increasingly important to modularize code by breaking it up into smaller functions of manageable size. You now hopefully have all the tools you need to do this."
]
},
@@ -5690,7 +5836,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/18 Namespaces and Scope in Python.ipynb b/Python/01. Basics/19 Namespaces and Scope in Python.ipynb
similarity index 94%
rename from python/01. Basics/18 Namespaces and Scope in Python.ipynb
rename to Python/01. Basics/19 Namespaces and Scope in Python.ipynb
index 2262a6e..4cffb14 100755
--- a/python/01. Basics/18 Namespaces and Scope in Python.ipynb
+++ b/Python/01. Basics/19 Namespaces and Scope in Python.ipynb
@@ -2,15 +2,50 @@
"cells": [
{
"cell_type": "markdown",
- "id": "c6235828",
"metadata": {},
"source": [
- "# Namespaces and Scope in Python"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Namespaces and Scope in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Namespaces in Python](#namespaces_in_python)\n",
+ " * [The Built-In Namespace](#the_built-in_namespace)\n",
+ " * [The Global Namespace](#the_global_namespace)\n",
+ " * [The Local and Enclosing Namespaces](#the_local_and_enclosing_namespaces)\n",
+ "* [Variable Scope](#variable_scope)\n",
+ " * [Examples](#examples)\n",
+ " * [Example 1: Single Definition](#example_1:_single_definition)\n",
+ " * [Example 2: Double Definition](#example_2:_double_definition)\n",
+ " * [Example 3: Triple Definition](#example_3:_triple_definition)\n",
+ " * [Example 4: No Definition](#example_4:_no_definition)\n",
+ "* [Python Namespace Dictionaries](#python_namespace_dictionaries)\n",
+ " * [The `globals()` function](#the_`globals()`_function)\n",
+ " * [The `locals()` function](#the_`locals()`_function)\n",
+ "* [Modify Variables Out of Scope](#modify_variables_out_of_scope)\n",
+ " * [The `global` Declaration](#the_`global`_declaration)\n",
+ " * [The `nonlocal` Declaration](#the_`nonlocal`_declaration)\n",
+ " * [Best Practices](#best_practices)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "a028f06d",
"metadata": {},
"source": [
"This tutorial covers Python **namespaces**, the structures used to organize the symbolic names assigned to objects in a Python program."
@@ -18,7 +53,6 @@
},
{
"cell_type": "markdown",
- "id": "4e9492a0",
"metadata": {},
"source": [
"The previous tutorials in this series have emphasized the importance of **objects** in Python. Objects are everywhere! Virtually everything that your Python program creates or acts on is an object."
@@ -26,7 +60,6 @@
},
{
"cell_type": "markdown",
- "id": "c99763b2",
"metadata": {},
"source": [
"An **assignment statement** creates a **symbolic name** that you can use to reference an object. The statement `x = 'foo'` creates a symbolic name `x` that refers to the string object `'foo'`."
@@ -34,7 +67,6 @@
},
{
"cell_type": "markdown",
- "id": "83ef4895",
"metadata": {},
"source": [
"In a program of any complexity, you’ll create hundreds or thousands of such names, each pointing to a specific object. How does Python keep track of all these names so that they don’t interfere with one another?"
@@ -42,7 +74,6 @@
},
{
"cell_type": "markdown",
- "id": "81d2f7da",
"metadata": {},
"source": [
"**In this tutorial, you’ll learn:**\n",
@@ -55,15 +86,15 @@
},
{
"cell_type": "markdown",
- "id": "1f664280",
"metadata": {},
"source": [
- "## Namespaces in Python"
+ "\n",
+ "## Namespaces in Python [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "6f991257",
"metadata": {},
"source": [
"A namespace is a collection of currently defined symbolic names along with information about the object that each name references. You can think of a namespace as a dictionary in which the keys are the object names and the values are the objects themselves. Each key-value pair maps a name to its corresponding object.\n",
@@ -75,7 +106,6 @@
},
{
"cell_type": "markdown",
- "id": "ceca6b92",
"metadata": {},
"source": [
"As Tim Peters suggests, namespaces aren’t just great. They’re honking great, and Python uses them extensively. In a Python program, there are four types of namespaces:\n",
@@ -88,7 +118,6 @@
},
{
"cell_type": "markdown",
- "id": "9d0fb592",
"metadata": {},
"source": [
"These have differing lifetimes. As Python executes a program, it creates namespaces as necessary and deletes them when they’re no longer needed. Typically, many namespaces will exist at any given time."
@@ -96,15 +125,15 @@
},
{
"cell_type": "markdown",
- "id": "877599cd",
"metadata": {},
"source": [
- "### The Built-In Namespace"
+ "\n",
+ "### The Built-In Namespace [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "42e1404c",
"metadata": {},
"source": [
"The **built-in namespace** contains the names of all of Python’s built-in objects. These are available at all times when Python is running. You can list the objects in the built-in namespace with the following command:"
@@ -112,8 +141,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
- "id": "bde62350",
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -122,8 +150,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
- "id": "eb711a46",
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
@@ -284,7 +311,7 @@
" 'zip']"
]
},
- "execution_count": 1,
+ "execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
@@ -295,7 +322,6 @@
},
{
"cell_type": "markdown",
- "id": "9731487f",
"metadata": {},
"source": [
"You’ll see some objects here that you may recognize from previous sections—for example, the `StopIteration` exception, built-in functions like `max()` and `len()`, and object types like `int` and `str`."
@@ -303,7 +329,6 @@
},
{
"cell_type": "markdown",
- "id": "b8ced286",
"metadata": {},
"source": [
"The Python interpreter creates the built-in namespace when it starts up. This namespace remains in existence until the interpreter terminates."
@@ -311,15 +336,15 @@
},
{
"cell_type": "markdown",
- "id": "7b676c6d",
"metadata": {},
"source": [
- "### The Global Namespace"
+ "\n",
+ "### The Global Namespace [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "2bc9db8a",
"metadata": {},
"source": [
"The **global namespace** contains any names defined at the level of the main program. Python creates the global namespace when the main program body starts, and it remains in existence until the interpreter terminates."
@@ -327,7 +352,6 @@
},
{
"cell_type": "markdown",
- "id": "0f7741d3",
"metadata": {},
"source": [
"Strictly speaking, this may not be the only global namespace that exists. The interpreter also creates a global namespace for any **module** that your program loads with the import statement. Further information on main functions and modules in Python will be covered later after OOP."
@@ -335,7 +359,6 @@
},
{
"cell_type": "markdown",
- "id": "20481be3",
"metadata": {},
"source": [
"You’ll explore modules in more detail in a future tutorial in this series. For the moment, when you see the term global namespace, think of the one belonging to the main program."
@@ -343,15 +366,15 @@
},
{
"cell_type": "markdown",
- "id": "8cb77bc7",
"metadata": {},
"source": [
- "### The Local and Enclosing Namespaces"
+ "\n",
+ "### The Local and Enclosing Namespaces [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "b4ffb90c",
"metadata": {},
"source": [
"As you learned in the previous tutorial on functions, the interpreter creates a new namespace whenever a function executes. That namespace is local to the function and remains in existence until the function terminates."
@@ -359,7 +382,6 @@
},
{
"cell_type": "markdown",
- "id": "c1fbe635",
"metadata": {},
"source": [
"Functions don’t exist independently from one another only at the level of the main program. You can also define one function inside another:"
@@ -368,7 +390,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "a17f3349",
"metadata": {},
"outputs": [
{
@@ -402,7 +423,6 @@
},
{
"cell_type": "markdown",
- "id": "4f02fdcd",
"metadata": {},
"source": [
"In this example, function `g()` is defined within the body of `f()`. Here’s what’s happening in this code:"
@@ -410,7 +430,6 @@
},
{
"cell_type": "markdown",
- "id": "7bd47654",
"metadata": {},
"source": [
"- **Lines 1 to 12** define `f()`, the **enclosing** function.\n",
@@ -421,7 +440,6 @@
},
{
"cell_type": "markdown",
- "id": "7184f8e9",
"metadata": {},
"source": [
"When the main program calls `f()`, Python creates a new namespace for `f()`. Similarly, when `f()` calls `g()`, `g()` gets its own separate namespace. The namespace created for g() is the **local namespace**, and the namespace created for `f()` is the **enclosing namespace**."
@@ -429,7 +447,6 @@
},
{
"cell_type": "markdown",
- "id": "268e730f",
"metadata": {},
"source": [
"Each of these namespaces remains in existence until its respective function terminates. Python might not immediately reclaim the memory allocated for those namespaces when their functions terminate, but all references to the objects they contain cease to be valid."
@@ -437,14 +454,15 @@
},
{
"cell_type": "code",
- "execution_count": 51,
- "id": "a383dbf7",
+ "execution_count": 66,
"metadata": {},
"outputs": [],
"source": [
"x = 2\n",
"def f():\n",
+ " x = 3\n",
" def g():\n",
+ " x = 2\n",
" print('here')\n",
" \n",
" g()"
@@ -452,8 +470,7 @@
},
{
"cell_type": "code",
- "execution_count": 54,
- "id": "122021dc",
+ "execution_count": 67,
"metadata": {},
"outputs": [
{
@@ -470,15 +487,15 @@
},
{
"cell_type": "markdown",
- "id": "eab720bb",
"metadata": {},
"source": [
- "## Variable Scope"
+ "\n",
+ "## Variable Scope [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "efe32377",
"metadata": {},
"source": [
"The existence of multiple, distinct namespaces means several different instances of a particular name can exist simultaneously while a Python program runs. As long as each instance is in a different namespace, they’re all maintained separately and won’t interfere with one another."
@@ -486,7 +503,6 @@
},
{
"cell_type": "markdown",
- "id": "aa551ac5",
"metadata": {},
"source": [
"But that raises a question: Suppose you refer to the name `x` in your code, and `x` exists in several namespaces. How does Python know which one you mean?"
@@ -494,7 +510,6 @@
},
{
"cell_type": "markdown",
- "id": "9941a464",
"metadata": {},
"source": [
"The answer lies in the concept of **scope**. The scope of a name is the region of a program in which that name has meaning. The interpreter determines this at runtime based on where the name definition occurs and where in the code the name is referenced."
@@ -502,7 +517,6 @@
},
{
"cell_type": "markdown",
- "id": "5d191e08",
"metadata": {},
"source": [
"> **Further Reading:** See the Wikipedia page on [scope in computer programming](https://en.wikipedia.org/wiki/Scope_(computer_science)) for a very thorough discussion of variable scope in programming languages."
@@ -510,7 +524,6 @@
},
{
"cell_type": "markdown",
- "id": "45bc8e30",
"metadata": {},
"source": [
"To return to the above question, if your code refers to the name `x`, then Python searches for `x` in the following namespaces in the order shown:\n",
@@ -523,7 +536,6 @@
},
{
"cell_type": "markdown",
- "id": "f1d5639b",
"metadata": {},
"source": [
"This is the **LEGB rule** as it’s commonly called in Python literature (although the term doesn’t actually appear in the Python documentation). The interpreter searches for a name from the inside out, looking in the **l**ocal, **e**nclosing, **g**lobal, and finally the **b**uilt-in scope:"
@@ -531,7 +543,6 @@
},
{
"cell_type": "markdown",
- "id": "cf5b9c10",
"metadata": {},
"source": [
"
"
@@ -539,7 +550,6 @@
},
{
"cell_type": "markdown",
- "id": "f9549966",
"metadata": {},
"source": [
"If the interpreter doesn’t find the name in any of these locations, then Python raises a `NameError` exception."
@@ -547,15 +557,15 @@
},
{
"cell_type": "markdown",
- "id": "8b720075",
"metadata": {},
"source": [
- "### Examples"
+ "\n",
+ "### Examples [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "72733510",
"metadata": {},
"source": [
"Several examples of the LEGB rule appear below. In each case, the innermost enclosed function `g()` attempts to display the value of a variable named `x` to the console. Notice how each example prints a different value for `x` depending on its scope."
@@ -563,15 +573,15 @@
},
{
"cell_type": "markdown",
- "id": "a7d924c7",
"metadata": {},
"source": [
- "#### Example 1: Single Definition"
+ "\n",
+ "#### Example 1: Single Definition [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "19b1471e",
"metadata": {},
"source": [
"In the first example, `x` is defined in only one location. It’s outside both `f()` and `g()`, so it resides in the global scope:"
@@ -580,7 +590,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "ed97108e",
"metadata": {},
"outputs": [
{
@@ -607,7 +616,6 @@
},
{
"cell_type": "markdown",
- "id": "c7bcb005",
"metadata": {},
"source": [
"The `print()` statement on **line 6** can refer to only one possible `x`. It displays the `x` object defined in the global namespace, which is the string `'global'`."
@@ -615,15 +623,15 @@
},
{
"cell_type": "markdown",
- "id": "3f948aad",
"metadata": {},
"source": [
- "#### Example 2: Double Definition"
+ "\n",
+ "#### Example 2: Double Definition [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "5848e940",
"metadata": {},
"source": [
"In the next example, the definition of `x` appears in two places, one outside `f()` and one inside `f()` but outside `g()`:"
@@ -632,7 +640,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "6149b768",
"metadata": {},
"outputs": [
{
@@ -660,7 +667,6 @@
},
{
"cell_type": "markdown",
- "id": "02f1992d",
"metadata": {},
"source": [
"As in the previous example, `g()` refers to `x`. But this time, it has two definitions to choose from:\n",
@@ -671,7 +677,6 @@
},
{
"cell_type": "markdown",
- "id": "66528abb",
"metadata": {},
"source": [
"According to the LEGB rule, the interpreter finds the value from the enclosing scope before looking in the global scope. So the `print()` statement on **line 7** displays `'enclosing'` instead of `'global'`."
@@ -679,15 +684,15 @@
},
{
"cell_type": "markdown",
- "id": "2c3d5007",
"metadata": {},
"source": [
- "#### Example 3: Triple Definition"
+ "\n",
+ "#### Example 3: Triple Definition [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "82393362",
"metadata": {},
"source": [
"Next is a situation in which `x` is defined here, there, and everywhere. One definition is outside `f()`, another one is inside `f()` but outside `g()`, and a third is inside `g()`:"
@@ -696,7 +701,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "59fd9d87",
"metadata": {},
"outputs": [
{
@@ -725,7 +729,6 @@
},
{
"cell_type": "markdown",
- "id": "887750ba",
"metadata": {},
"source": [
"Now the `print()` statement on **line 8** has to distinguish between three different possibilities:\n",
@@ -737,7 +740,6 @@
},
{
"cell_type": "markdown",
- "id": "27ab09d7",
"metadata": {},
"source": [
"Here, the LEGB rule dictates that `g()` sees its own locally defined value of `x` first. So the `print()` statement displays `'local'`."
@@ -745,15 +747,15 @@
},
{
"cell_type": "markdown",
- "id": "f99635e0",
"metadata": {},
"source": [
- "#### Example 4: No Definition"
+ "\n",
+ "#### Example 4: No Definition [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "baeac336",
"metadata": {},
"source": [
"Last, we have a case in which `g()` tries to print the value of `y`, but `y` isn’t defined anywhere. That won’t work at all:"
@@ -762,7 +764,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "7df73202",
"metadata": {},
"outputs": [
{
@@ -793,7 +794,6 @@
},
{
"cell_type": "markdown",
- "id": "80947d46",
"metadata": {},
"source": [
"This time, Python doesn’t find `x` in any of the namespaces, so the `print()` statement on line 4 generates a `NameError` exception."
@@ -801,15 +801,15 @@
},
{
"cell_type": "markdown",
- "id": "3d3a7e62",
"metadata": {},
"source": [
- "## Python Namespace Dictionaries"
+ "\n",
+ "## Python Namespace Dictionaries [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "3e2d1c49",
"metadata": {},
"source": [
"Earlier in this tutorial, when namespaces were first introduced, you were encouraged to think of a namespace as a dictionary in which the keys are the object names and the values are the objects themselves. In fact, for global and local namespaces, that’s precisely what they are! Python really does implement these namespaces as dictionaries."
@@ -817,7 +817,6 @@
},
{
"cell_type": "markdown",
- "id": "a47df216",
"metadata": {},
"source": [
"> **Note:** The built-in namespace doesn’t behave like a dictionary. Python implements it as a module. We will cover modules later."
@@ -825,7 +824,6 @@
},
{
"cell_type": "markdown",
- "id": "aa442775",
"metadata": {},
"source": [
"Python provides built-in functions called `globals()` and `locals()` that allow you to access global and local namespace dictionaries."
@@ -833,15 +831,15 @@
},
{
"cell_type": "markdown",
- "id": "3c3e70e0",
"metadata": {},
"source": [
- "### The `globals()` function"
+ "\n",
+ "### The `globals()` function [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "10df7178",
"metadata": {},
"source": [
"The built-in function `globals()` returns a reference to the current global namespace dictionary. You can use it to access the objects in the global namespace. Here’s an example of what it looks like when the main program starts:"
@@ -850,7 +848,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "a45523b6",
"metadata": {},
"outputs": [
{
@@ -871,7 +868,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "4f282400",
"metadata": {},
"outputs": [
{
@@ -1567,7 +1563,6 @@
},
{
"cell_type": "markdown",
- "id": "804b2b4f",
"metadata": {},
"source": [
"As you can see, the interpreter has put several entries in `globals()` already. Depending on your Python version and operating system, it may look a little different in your environment. But it should be similar."
@@ -1575,7 +1570,6 @@
},
{
"cell_type": "markdown",
- "id": "a21ec971",
"metadata": {},
"source": [
"Now watch what happens when you define a variable in the global scope:"
@@ -1584,7 +1578,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "dc800d60",
"metadata": {},
"outputs": [],
"source": [
@@ -1594,7 +1587,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "a37647ce",
"metadata": {},
"outputs": [
{
@@ -2159,7 +2151,6 @@
},
{
"cell_type": "markdown",
- "id": "dbe91948",
"metadata": {},
"source": [
"After the assignment statement `x = 'foo'`, a new item appears in the global namespace dictionary. The dictionary key is the object’s name, `x`, and the dictionary value is the object’s value, `'foo'`."
@@ -2167,7 +2158,6 @@
},
{
"cell_type": "markdown",
- "id": "59f8e70f",
"metadata": {},
"source": [
"You would typically access this object in the usual way, by referring to its symbolic name, `x`. But you can also access it indirectly through the global namespace dictionary:"
@@ -2176,7 +2166,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "9f66b358",
"metadata": {},
"outputs": [
{
@@ -2197,7 +2186,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "cee88738",
"metadata": {},
"outputs": [
{
@@ -2218,7 +2206,6 @@
{
"cell_type": "code",
"execution_count": 81,
- "id": "bdad6409",
"metadata": {},
"outputs": [
{
@@ -2239,7 +2226,6 @@
},
{
"cell_type": "markdown",
- "id": "34ccfcda",
"metadata": {},
"source": [
"The `is` comparison on **line 6** confirms that these are in fact the same object."
@@ -2247,7 +2233,6 @@
},
{
"cell_type": "markdown",
- "id": "5408952d",
"metadata": {},
"source": [
"You can create and modify entries in the global namespace using the `globals()` function as well:"
@@ -2256,7 +2241,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "dd4f6759",
"metadata": {},
"outputs": [],
"source": [
@@ -2266,7 +2250,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "979f8193",
"metadata": {},
"outputs": [
{
@@ -2868,17 +2851,16 @@
},
{
"cell_type": "code",
- "execution_count": 26,
- "id": "1f6b8cd3",
+ "execution_count": 72,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "100"
+ "4"
]
},
- "execution_count": 26,
+ "execution_count": 72,
"metadata": {},
"output_type": "execute_result"
}
@@ -2889,8 +2871,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
- "id": "ceab9647",
+ "execution_count": 73,
"metadata": {},
"outputs": [],
"source": [
@@ -2899,8 +2880,7 @@
},
{
"cell_type": "code",
- "execution_count": 28,
- "id": "e940245b",
+ "execution_count": 74,
"metadata": {},
"outputs": [
{
@@ -2909,7 +2889,7 @@
"3.14159"
]
},
- "execution_count": 28,
+ "execution_count": 74,
"metadata": {},
"output_type": "execute_result"
}
@@ -2920,7 +2900,6 @@
},
{
"cell_type": "markdown",
- "id": "b8bf7520",
"metadata": {},
"source": [
"The statement on **line 1** has the same effect as the assignment statement `y = 100`. The statement on **line 12** is equivalent to `y = 3.14159`."
@@ -2928,7 +2907,6 @@
},
{
"cell_type": "markdown",
- "id": "4dc9afc8",
"metadata": {},
"source": [
"It’s a little off the beaten path to create and modify objects in the global scope this way when simple assignment statements will do. But it works, and it illustrates the concept nicely."
@@ -2936,15 +2914,15 @@
},
{
"cell_type": "markdown",
- "id": "91cfc189",
"metadata": {},
"source": [
- "### The `locals()` function"
+ "\n",
+ "### The `locals()` function [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "c9de4863",
"metadata": {},
"source": [
"Python also provides a corresponding built-in function called `locals()`. It’s similar to `globals()` but accesses objects in the local namespace instead:"
@@ -2953,7 +2931,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "839ef52d",
"metadata": {},
"outputs": [],
"source": [
@@ -2965,7 +2942,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "22cb1b17",
"metadata": {},
"outputs": [
{
@@ -2982,7 +2958,6 @@
},
{
"cell_type": "markdown",
- "id": "42c8045a",
"metadata": {},
"source": [
"When called within `f()`, `locals()` returns a dictionary representing the function’s local namespace. Notice that, in addition to the locally defined variable `s`, the local namespace includes the function parameters `x` and `y` since these are local to `f()` as well."
@@ -2990,7 +2965,6 @@
},
{
"cell_type": "markdown",
- "id": "3aee7d3f",
"metadata": {},
"source": [
"If you call `locals()` outside a function in the main program, then it behaves the same as `globals()`."
@@ -2999,7 +2973,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "205e9017",
"metadata": {},
"outputs": [
{
@@ -3628,7 +3601,6 @@
},
{
"cell_type": "markdown",
- "id": "b56fb01e",
"metadata": {},
"source": [
"> Deep Dive: A Subtle Difference Between `globals()` and `locals()`\n",
@@ -3680,15 +3652,15 @@
},
{
"cell_type": "markdown",
- "id": "a48a780f",
"metadata": {},
"source": [
- "## Modify Variables Out of Scope"
+ "\n",
+ "## Modify Variables Out of Scope [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "e8f4fa2c",
"metadata": {},
"source": [
"Earlier in this series, in the tutorial on user-defined Python functions, you learned that argument passing in Python is a bit like pass-by-value and a bit like pass-by-reference. Sometimes a function can modify its argument in the calling environment by making changes to the corresponding parameter, and sometimes it can’t:\n",
@@ -3699,7 +3671,6 @@
},
{
"cell_type": "markdown",
- "id": "f322df49",
"metadata": {},
"source": [
"A similar situation exists when a function tries to modify a variable outside its local scope. A function can’t modify an immutable object outside its local scope at all:"
@@ -3708,7 +3679,6 @@
{
"cell_type": "code",
"execution_count": 106,
- "id": "f91f1093",
"metadata": {},
"outputs": [],
"source": [
@@ -3721,7 +3691,6 @@
{
"cell_type": "code",
"execution_count": 107,
- "id": "14fd529b",
"metadata": {},
"outputs": [
{
@@ -3739,7 +3708,6 @@
{
"cell_type": "code",
"execution_count": 108,
- "id": "c4cb91c3",
"metadata": {},
"outputs": [
{
@@ -3759,7 +3727,6 @@
},
{
"cell_type": "markdown",
- "id": "f2834af0",
"metadata": {},
"source": [
"When `f()` executes the assignment `x = 40` on **line 3**, it creates a new local reference to an integer object whose value is 40. At that point, `f()` loses the reference to the object named `x` in the global namespace. So the assignment statement doesn’t affect the global object."
@@ -3767,7 +3734,6 @@
},
{
"cell_type": "markdown",
- "id": "5b8ae12a",
"metadata": {},
"source": [
"Note that when `f()` executes `print(x)` on **line 4**, it displays `40`, the value of its own local `x`. But after `f()` terminates, `x` in the global scope is still `20`."
@@ -3775,7 +3741,6 @@
},
{
"cell_type": "markdown",
- "id": "601221a3",
"metadata": {},
"source": [
"A function can modify an object of mutable type that’s outside its local scope if it modifies the object in place:"
@@ -3784,7 +3749,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "d8937d16",
"metadata": {},
"outputs": [],
"source": [
@@ -3796,7 +3760,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "f59de355",
"metadata": {},
"outputs": [],
"source": [
@@ -3806,7 +3769,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "8099a498",
"metadata": {},
"outputs": [
{
@@ -3826,7 +3788,6 @@
},
{
"cell_type": "markdown",
- "id": "f6951de7",
"metadata": {},
"source": [
"In this case, `my_list` is a list, and lists are mutable. `f()` can make changes inside `my_list` even though it’s outside the local scope."
@@ -3834,7 +3795,6 @@
},
{
"cell_type": "markdown",
- "id": "ac1e1c42",
"metadata": {},
"source": [
"But if `f()` tries to reassign my_list entirely, then it will create a new local object and won’t modify the global `my_list`:"
@@ -3843,7 +3803,6 @@
{
"cell_type": "code",
"execution_count": 35,
- "id": "a6475d67",
"metadata": {},
"outputs": [],
"source": [
@@ -3855,7 +3814,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "d2f02798",
"metadata": {},
"outputs": [],
"source": [
@@ -3865,7 +3823,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "bbbf7a46",
"metadata": {},
"outputs": [
{
@@ -3885,7 +3842,6 @@
},
{
"cell_type": "markdown",
- "id": "3a1144f8",
"metadata": {},
"source": [
"This is similar to what happens when `f()` tries to modify a mutable function argument."
@@ -3893,15 +3849,26 @@
},
{
"cell_type": "markdown",
- "id": "9adee69e",
"metadata": {},
"source": [
- "### The `global` Declaration"
+ "\n",
+ "### The `global` Declaration [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def add(a, b):\n",
+ " x = 3\n",
+ " str(2)"
]
},
{
"cell_type": "markdown",
- "id": "5069956c",
"metadata": {},
"source": [
"What if you really do need to modify a value in the global scope from within `f()`? This is possible in Python using the `global` declaration:"
@@ -3909,7 +3876,6 @@
},
{
"cell_type": "markdown",
- "id": "55baf3f1",
"metadata": {},
"source": [
"The global `x` statement indicates that while `f()` executes, references to the name `x` will refer to the `x` that is in the global namespace. That means the assignment `x = 40` doesn’t create a new reference. It assigns a new value to `x` in the global scope instead:"
@@ -3917,7 +3883,6 @@
},
{
"cell_type": "markdown",
- "id": "1b2a9885",
"metadata": {},
"source": [
"
"
@@ -3925,7 +3890,6 @@
},
{
"cell_type": "markdown",
- "id": "9c162e64",
"metadata": {},
"source": [
"As you’ve already seen, `globals()` returns a reference to the global namespace dictionary. If you wanted to, instead of using a global statement, you could accomplish the same thing using `globals()`:"
@@ -3934,7 +3898,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "abd1393a",
"metadata": {},
"outputs": [],
"source": [
@@ -3947,7 +3910,6 @@
{
"cell_type": "code",
"execution_count": 45,
- "id": "75e13536",
"metadata": {},
"outputs": [
{
@@ -3965,7 +3927,6 @@
{
"cell_type": "code",
"execution_count": 46,
- "id": "ea040aeb",
"metadata": {},
"outputs": [
{
@@ -3985,7 +3946,6 @@
},
{
"cell_type": "markdown",
- "id": "8cbb2515",
"metadata": {},
"source": [
"There isn’t much reason to do it this way since the `global` declaration arguably makes the intent clearer. But it does provide another illustration of how `globals()` works."
@@ -3993,7 +3953,6 @@
},
{
"cell_type": "markdown",
- "id": "eb5e253b",
"metadata": {},
"source": [
"If the name specified in the `global` declaration doesn’t exist in the global scope when the function starts, then a combination of the `global` statement and an assignment will create it:"
@@ -4002,7 +3961,6 @@
{
"cell_type": "code",
"execution_count": 144,
- "id": "1e3cdf50",
"metadata": {},
"outputs": [
{
@@ -4024,7 +3982,6 @@
{
"cell_type": "code",
"execution_count": 145,
- "id": "b84befab",
"metadata": {},
"outputs": [],
"source": [
@@ -4036,7 +3993,6 @@
{
"cell_type": "code",
"execution_count": 146,
- "id": "6e32c7f8",
"metadata": {},
"outputs": [],
"source": [
@@ -4046,7 +4002,6 @@
{
"cell_type": "code",
"execution_count": 147,
- "id": "babca851",
"metadata": {},
"outputs": [
{
@@ -4066,7 +4021,6 @@
},
{
"cell_type": "markdown",
- "id": "3b09649d",
"metadata": {},
"source": [
"In this case, there’s no object named `z` in the global scope when `g()` starts, but `g()` creates one with the global `y` statement on **line 8**."
@@ -4074,7 +4028,6 @@
},
{
"cell_type": "markdown",
- "id": "a00a68a3",
"metadata": {},
"source": [
"You can also specify several comma-separated names in a single `global` declaration:"
@@ -4083,7 +4036,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "f55a0754",
"metadata": {},
"outputs": [],
"source": [
@@ -4095,7 +4047,6 @@
},
{
"cell_type": "markdown",
- "id": "d76e3b77",
"metadata": {},
"source": [
"Here, `x`, `y`, and `z` are all declared to refer to objects in the global scope by the single `global` statement on **line 4**."
@@ -4103,7 +4054,6 @@
},
{
"cell_type": "markdown",
- "id": "3197e014",
"metadata": {},
"source": [
"A name specified in a `global` declaration can’t appear in the function prior to the `global` statement:"
@@ -4112,7 +4062,6 @@
{
"cell_type": "code",
"execution_count": 62,
- "id": "ec807aea",
"metadata": {},
"outputs": [
{
@@ -4132,7 +4081,6 @@
},
{
"cell_type": "markdown",
- "id": "bbb90ea5",
"metadata": {},
"source": [
"The intent of the global `x` statement on **line 3** is to make references to `x` refer to an object in the global scope. But the `print()` statement on **line 2** refers to `x` to prior to the `global` declaration. This raises a `SyntaxError` exception."
@@ -4140,15 +4088,15 @@
},
{
"cell_type": "markdown",
- "id": "eecc479a",
"metadata": {},
"source": [
- "### The `nonlocal` Declaration"
+ "\n",
+ "### The `nonlocal` Declaration [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "b3444918",
"metadata": {},
"source": [
"A similar situation exists with nested function definitions. The `global` declaration allows a function to access and modify an object in the global scope. What if an enclosed function needs to modify an object in the enclosing scope? Consider this example:"
@@ -4157,7 +4105,6 @@
{
"cell_type": "code",
"execution_count": 63,
- "id": "3e16de43",
"metadata": {},
"outputs": [
{
@@ -4184,7 +4131,6 @@
},
{
"cell_type": "markdown",
- "id": "0beb8d7c",
"metadata": {},
"source": [
"In this case, the first definition of `x` is in the enclosing scope, not the global scope. Just as `g()` can’t directly modify a variable in the global scope, neither can it modify `x` in the enclosing function’s scope. Following the assignment `x = 40` on **line 5**, `x` in the enclosing scope remains `20`."
@@ -4192,7 +4138,6 @@
},
{
"cell_type": "markdown",
- "id": "de9c5356",
"metadata": {},
"source": [
"The `global` keyword isn’t a solution for this situation:"
@@ -4201,7 +4146,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "168b4dd4",
"metadata": {},
"outputs": [
{
@@ -4229,7 +4173,6 @@
},
{
"cell_type": "markdown",
- "id": "eba94c65",
"metadata": {},
"source": [
"Since `x` is in the enclosing function’s scope, not the global scope, the `global` keyword doesn’t work here. After `g()` terminates, `x` in the enclosing scope remains `20`."
@@ -4237,7 +4180,6 @@
},
{
"cell_type": "markdown",
- "id": "2332d696",
"metadata": {},
"source": [
"In fact, in this example, the global `x` statement not only fails to provide access to `x` in the enclosing scope, but it also creates an object called `x` in the global scope whose value is `40`:"
@@ -4246,7 +4188,6 @@
{
"cell_type": "code",
"execution_count": 66,
- "id": "9e718c5d",
"metadata": {},
"outputs": [],
"source": [
@@ -4264,7 +4205,6 @@
{
"cell_type": "code",
"execution_count": 67,
- "id": "48a60e71",
"metadata": {},
"outputs": [
{
@@ -4282,7 +4222,6 @@
{
"cell_type": "code",
"execution_count": 68,
- "id": "ea338cea",
"metadata": {},
"outputs": [
{
@@ -4302,7 +4241,6 @@
},
{
"cell_type": "markdown",
- "id": "5a5eeb54",
"metadata": {},
"source": [
"To modify `x` in the enclosing scope from inside `g()`, you need the analogous keyword `nonlocal`. Names specified after the `nonlocal` keyword refer to variables in the nearest enclosing scope:"
@@ -4310,8 +4248,24 @@
},
{
"cell_type": "code",
- "execution_count": 69,
- "id": "7cf50267",
+ "execution_count": 189,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def f():\n",
+ " x = 20\n",
+ "\n",
+ " def g():\n",
+ " nonlocal x\n",
+ " x = 40\n",
+ "\n",
+ " g()\n",
+ " print(x)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 190,
"metadata": {},
"outputs": [
{
@@ -4323,23 +4277,11 @@
}
],
"source": [
- "def f():\n",
- " x = 20\n",
- "\n",
- " def g():\n",
- " nonlocal x\n",
- " x = 40\n",
- "\n",
- " g()\n",
- " print(x)\n",
- "\n",
- "\n",
"f()"
]
},
{
"cell_type": "markdown",
- "id": "486cdcc4",
"metadata": {},
"source": [
"After the nonlocal `x` statement on **line 5**, when `g()` refers to `x`, it refers to the `x` in the nearest enclosing scope, whose definition is in `f()` on **line 2**:"
@@ -4347,7 +4289,6 @@
},
{
"cell_type": "markdown",
- "id": "9ee49926",
"metadata": {},
"source": [
"
"
@@ -4355,7 +4296,6 @@
},
{
"cell_type": "markdown",
- "id": "58448c2c",
"metadata": {},
"source": [
"The `print()` statement at the end of `f()` on **line 9** confirms that the call to `g()` has changed the value of `x` in the enclosing scope to `40`."
@@ -4363,15 +4303,15 @@
},
{
"cell_type": "markdown",
- "id": "881dbef0",
"metadata": {},
"source": [
- "### Best Practices"
+ "\n",
+ "### Best Practices [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "bd155eb1",
"metadata": {},
"source": [
"Even though Python provides the `global` and `nonlocal` keywords, it’s not advisable to use them."
@@ -4379,7 +4319,6 @@
},
{
"cell_type": "markdown",
- "id": "5e93a482",
"metadata": {},
"source": [
"When a function modifies data outside the local scope, either with the `global` or `nonlocal` keyword or by directly modifying a mutable type in place, it’s a kind of side effect similar to when a function modifies one of its arguments. Widespread modification of global variables is generally considered unwise, not only in Python but also in other programming languages."
@@ -4387,7 +4326,6 @@
},
{
"cell_type": "markdown",
- "id": "c8f4d23c",
"metadata": {},
"source": [
"In Python, using the `global` keyword at least makes it explicit that the function is modifying a global variable. In many languages, a function can modify a global variable just by assignment, without announcing it in any way. This can make it very difficult to track down where global data is being modified."
@@ -4395,23 +4333,57 @@
},
{
"cell_type": "markdown",
- "id": "252e0be2",
"metadata": {},
"source": [
"All in all, modifying variables outside the local scope usually isn’t necessary. There’s almost always a better way, usually with function return values."
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 163,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = 4\n",
+ "def f():\n",
+ " print(x)\n",
+ " x = 3\n",
+ " print(x) "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 164,
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "UnboundLocalError",
+ "evalue": "local variable 'x' referenced before assignment",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36mf\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'x' referenced before assignment"
+ ]
+ }
+ ],
+ "source": [
+ "f()"
+ ]
+ },
{
"cell_type": "markdown",
- "id": "ed9768bd",
"metadata": {},
"source": [
- "## Conclusion"
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "3fba931f",
"metadata": {},
"source": [
"Virtually everything that a Python program uses or acts on is an object. Even a short program will create many different objects. In a more complex program, they’ll probably number in the thousands. Python has to keep track of all these objects and their names, and it does so with **namespaces**."
@@ -4419,7 +4391,6 @@
},
{
"cell_type": "markdown",
- "id": "e3524308",
"metadata": {},
"source": [
"**In this tutorial, you learned:**\n",
@@ -4432,7 +4403,6 @@
},
{
"cell_type": "markdown",
- "id": "bf1caa1a",
"metadata": {},
"source": [
"Many programming techniques take advantage of the fact that every function in Python has its own namespace. Later in other sections, you’ll explore two of these techniques: **functional programming** and **recursion**."
@@ -4455,7 +4425,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/01. Basics/images/break-continue.png b/Python/01. Basics/images/break-continue.png
similarity index 100%
rename from python/01. Basics/images/break-continue.png
rename to Python/01. Basics/images/break-continue.png
diff --git a/python/01. Basics/images/celebrate.png b/Python/01. Basics/images/celebrate.png
similarity index 100%
rename from python/01. Basics/images/celebrate.png
rename to Python/01. Basics/images/celebrate.png
diff --git a/python/01. Basics/images/conda-not-found.png b/Python/01. Basics/images/conda-not-found.png
similarity index 100%
rename from python/01. Basics/images/conda-not-found.png
rename to Python/01. Basics/images/conda-not-found.png
diff --git a/python/01. Basics/images/dictionary.webp b/Python/01. Basics/images/dictionary.webp
similarity index 100%
rename from python/01. Basics/images/dictionary.webp
rename to Python/01. Basics/images/dictionary.webp
diff --git a/python/01. Basics/images/f-strings-fast.png b/Python/01. Basics/images/f-strings-fast.png
similarity index 100%
rename from python/01. Basics/images/f-strings-fast.png
rename to Python/01. Basics/images/f-strings-fast.png
diff --git a/python/01. Basics/images/f-strings-speed.png b/Python/01. Basics/images/f-strings-speed.png
similarity index 100%
rename from python/01. Basics/images/f-strings-speed.png
rename to Python/01. Basics/images/f-strings-speed.png
diff --git a/python/01. Basics/images/flat-v-container.png b/Python/01. Basics/images/flat-v-container.png
similarity index 100%
rename from python/01. Basics/images/flat-v-container.png
rename to Python/01. Basics/images/flat-v-container.png
diff --git a/python/01. Basics/images/for-loop.webp b/Python/01. Basics/images/for-loop.webp
similarity index 100%
rename from python/01. Basics/images/for-loop.webp
rename to Python/01. Basics/images/for-loop.webp
diff --git a/python/01. Basics/images/format-2.webp b/Python/01. Basics/images/format-2.webp
similarity index 100%
rename from python/01. Basics/images/format-2.webp
rename to Python/01. Basics/images/format-2.webp
diff --git a/python/01. Basics/images/format.webp b/Python/01. Basics/images/format.webp
similarity index 100%
rename from python/01. Basics/images/format.webp
rename to Python/01. Basics/images/format.webp
diff --git a/python/01. Basics/images/func-arguments.webp b/Python/01. Basics/images/func-arguments.webp
similarity index 100%
rename from python/01. Basics/images/func-arguments.webp
rename to Python/01. Basics/images/func-arguments.webp
diff --git a/python/01. Basics/images/func.webp b/Python/01. Basics/images/func.webp
similarity index 100%
rename from python/01. Basics/images/func.webp
rename to Python/01. Basics/images/func.webp
diff --git a/python/01. Basics/images/global.webp b/Python/01. Basics/images/global.webp
similarity index 100%
rename from python/01. Basics/images/global.webp
rename to Python/01. Basics/images/global.webp
diff --git a/python/01. Basics/images/if-block-c.png b/Python/01. Basics/images/if-block-c.png
similarity index 100%
rename from python/01. Basics/images/if-block-c.png
rename to Python/01. Basics/images/if-block-c.png
diff --git a/python/01. Basics/images/if-block.png b/Python/01. Basics/images/if-block.png
similarity index 100%
rename from python/01. Basics/images/if-block.png
rename to Python/01. Basics/images/if-block.png
diff --git a/python/01. Basics/images/int-pointer-2.png b/Python/01. Basics/images/int-pointer-2.png
similarity index 100%
rename from python/01. Basics/images/int-pointer-2.png
rename to Python/01. Basics/images/int-pointer-2.png
diff --git a/python/01. Basics/images/int-pointer-m-n.png b/Python/01. Basics/images/int-pointer-m-n.png
similarity index 100%
rename from python/01. Basics/images/int-pointer-m-n.png
rename to Python/01. Basics/images/int-pointer-m-n.png
diff --git a/python/01. Basics/images/int-pointer.png b/Python/01. Basics/images/int-pointer.png
similarity index 100%
rename from python/01. Basics/images/int-pointer.png
rename to Python/01. Basics/images/int-pointer.png
diff --git a/python/01. Basics/images/int-string-pointer.png b/Python/01. Basics/images/int-string-pointer.png
similarity index 100%
rename from python/01. Basics/images/int-string-pointer.png
rename to Python/01. Basics/images/int-string-pointer.png
diff --git a/python/01. Basics/images/intersection.webp b/Python/01. Basics/images/intersection.webp
similarity index 100%
rename from python/01. Basics/images/intersection.webp
rename to Python/01. Basics/images/intersection.webp
diff --git a/python/01. Basics/images/legb.webp b/Python/01. Basics/images/legb.webp
similarity index 100%
rename from python/01. Basics/images/legb.webp
rename to Python/01. Basics/images/legb.webp
diff --git a/python/01. Basics/images/line-wrap.webp b/Python/01. Basics/images/line-wrap.webp
similarity index 100%
rename from python/01. Basics/images/line-wrap.webp
rename to Python/01. Basics/images/line-wrap.webp
diff --git a/python/01. Basics/images/list-in-list.webp b/Python/01. Basics/images/list-in-list.webp
similarity index 100%
rename from python/01. Basics/images/list-in-list.webp
rename to Python/01. Basics/images/list-in-list.webp
diff --git a/python/01. Basics/images/list-negative-index.webp b/Python/01. Basics/images/list-negative-index.webp
similarity index 100%
rename from python/01. Basics/images/list-negative-index.webp
rename to Python/01. Basics/images/list-negative-index.webp
diff --git a/python/01. Basics/images/list.webp b/Python/01. Basics/images/list.webp
similarity index 100%
rename from python/01. Basics/images/list.webp
rename to Python/01. Basics/images/list.webp
diff --git a/python/01. Basics/images/nonlocal.png b/Python/01. Basics/images/nonlocal.png
similarity index 100%
rename from python/01. Basics/images/nonlocal.png
rename to Python/01. Basics/images/nonlocal.png
diff --git a/python/01. Basics/images/python-shell.png b/Python/01. Basics/images/python-shell.png
similarity index 100%
rename from python/01. Basics/images/python-shell.png
rename to Python/01. Basics/images/python-shell.png
diff --git a/python/01. Basics/images/string-indexing-negative.png b/Python/01. Basics/images/string-indexing-negative.png
similarity index 100%
rename from python/01. Basics/images/string-indexing-negative.png
rename to Python/01. Basics/images/string-indexing-negative.png
diff --git a/python/01. Basics/images/string-indexing.png b/Python/01. Basics/images/string-indexing.png
similarity index 100%
rename from python/01. Basics/images/string-indexing.png
rename to Python/01. Basics/images/string-indexing.png
diff --git a/python/01. Basics/images/string-slicing-stride-2.PNG b/Python/01. Basics/images/string-slicing-stride-2.PNG
similarity index 100%
rename from python/01. Basics/images/string-slicing-stride-2.PNG
rename to Python/01. Basics/images/string-slicing-stride-2.PNG
diff --git a/python/01. Basics/images/string-slicing-stride.png b/Python/01. Basics/images/string-slicing-stride.png
similarity index 100%
rename from python/01. Basics/images/string-slicing-stride.png
rename to Python/01. Basics/images/string-slicing-stride.png
diff --git a/python/01. Basics/images/string-slicing.png b/Python/01. Basics/images/string-slicing.png
similarity index 100%
rename from python/01. Basics/images/string-slicing.png
rename to Python/01. Basics/images/string-slicing.png
diff --git a/python/01. Basics/images/tuple-packing.webp b/Python/01. Basics/images/tuple-packing.webp
similarity index 100%
rename from python/01. Basics/images/tuple-packing.webp
rename to Python/01. Basics/images/tuple-packing.webp
diff --git a/python/01. Basics/images/tuple-unpacking.webp b/Python/01. Basics/images/tuple-unpacking.webp
similarity index 100%
rename from python/01. Basics/images/tuple-unpacking.webp
rename to Python/01. Basics/images/tuple-unpacking.webp
diff --git a/python/01. Basics/images/union.png b/Python/01. Basics/images/union.png
similarity index 100%
rename from python/01. Basics/images/union.png
rename to Python/01. Basics/images/union.png
diff --git a/python/01. Basics/images/variable-passing-func-1.webp b/Python/01. Basics/images/variable-passing-func-1.webp
similarity index 100%
rename from python/01. Basics/images/variable-passing-func-1.webp
rename to Python/01. Basics/images/variable-passing-func-1.webp
diff --git a/python/01. Basics/images/variable-passing-func-2.webp b/Python/01. Basics/images/variable-passing-func-2.webp
similarity index 100%
rename from python/01. Basics/images/variable-passing-func-2.webp
rename to Python/01. Basics/images/variable-passing-func-2.webp
diff --git a/python/02. Object Oriented Programming/01 OOP Paradigm.ipynb b/Python/02. Object Oriented Programming/01 OOP Paradigm.ipynb
similarity index 69%
rename from python/02. Object Oriented Programming/01 OOP Paradigm.ipynb
rename to Python/02. Object Oriented Programming/01 OOP Paradigm.ipynb
index 2b897a3..5a9e841 100755
--- a/python/02. Object Oriented Programming/01 OOP Paradigm.ipynb
+++ b/Python/02. Object Oriented Programming/01 OOP Paradigm.ipynb
@@ -4,7 +4,34 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# OOP Paradigm"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
OOP Paradigm \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Programming Paradigms](#programming_paradigms)\n",
+ "* [Object Oriented Python](#object_oriented_python)\n",
+ "* [Modules vs. Classes](#modules_vs._classes)\n",
+ "* [Classes, Instances, Type, Methods, and Attributes](#classes,_instances,_type,_methods,_and_attributes)\n",
+ "* [Functions vs. Methods](#functions_vs._methods)\n",
+ " * [Functions](#functions)\n",
+ " * [Methods](#methods)\n",
+ " * [Key differences between method and function in python](#key_differences_between_method_and_function_in_python)\n",
+ "\n",
+ "---"
]
},
{
@@ -24,7 +51,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Programming Paradigms\n",
+ "\n",
+ "## Programming Paradigms [
](#table_of_contents)\n",
"\n",
"The term programming paradigm refers to a style of programming. It does not refer to a specific language, but rather it refers to the way you program. A programming paradigm is an approach to solve the problem using some programming language \n",
"\n",
@@ -39,7 +67,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Object Oriented Python\n",
+ "\n",
+ "## Object Oriented Python [
](#table_of_contents)\n",
+ "\n",
"\n",
"- Everything is an object, even numbers.\n",
"- All entities in Python follow the same rules of objects:\n",
@@ -51,7 +81,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Modules vs. Classes\n",
+ "\n",
+ "## Modules vs. Classes [
](#table_of_contents)\n",
+ "\n",
"\n",
"- Python modules are files that contain Python code.\n",
"- Python modules can be executed or imported.\n",
@@ -63,7 +95,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Classes, Instances, Type, Methods, and Attributes\n",
+ "\n",
+ "## Classes, Instances, Type, Methods, and Attributes [
](#table_of_contents)\n",
+ "\n",
"\n",
"- Class: a blueprint of an instance.\n",
"- Instance: a constructed object of the class.\n",
@@ -124,14 +158,17 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Functions vs. Methods"
+ "\n",
+ "## Functions vs. Methods [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Functions\n",
+ "\n",
+ "### Functions [
](#table_of_contents)\n",
+ "\n",
"\n",
"A **function** is a block of code to carry out a specific task, will contain its own scope and is called by name. All functions may contain zero(no) arguments or more than one arguments. On exit, a function can or can not return one or more values.\n",
"\n",
@@ -154,7 +191,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Methods"
+ "\n",
+ "### Methods [
](#table_of_contents)\n"
]
},
{
@@ -187,7 +225,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Key differences between method and function in python\n",
+ "\n",
+ "### Key differences between method and function in python [
](#table_of_contents)\n",
+ "\n",
"\n",
"As we get the basic understanding of the function and method both, let's highlight the key differences between them :\n",
"\n",
@@ -220,7 +260,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/02. Object Oriented Programming/02 OOP in Python.ipynb b/Python/02. Object Oriented Programming/02 OOP in Python.ipynb
similarity index 91%
rename from python/02. Object Oriented Programming/02 OOP in Python.ipynb
rename to Python/02. Object Oriented Programming/02 OOP in Python.ipynb
index 1523d79..beb8910 100755
--- a/python/02. Object Oriented Programming/02 OOP in Python.ipynb
+++ b/Python/02. Object Oriented Programming/02 OOP in Python.ipynb
@@ -2,15 +2,41 @@
"cells": [
{
"cell_type": "markdown",
- "id": "3c24cd04",
"metadata": {},
"source": [
- "# Object Oriented Programming in Python"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Object Oriented Programming in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [What Is Object-Oriented Programming in Python?](#what_is_object-oriented_programming_in_python?)\n",
+ "* [Define a Class in Python](#define_a_class_in_python)\n",
+ "* [Classes vs Instances](#classes_vs_instances)\n",
+ " * [How to Define a Class](#how_to_define_a_class)\n",
+ "* [Instantiate an Object in Python](#instantiate_an_object_in_python)\n",
+ "* [Class and Instance Attributes](#class_and_instance_attributes)\n",
+ " * [Instance Methods](#instance_methods)\n",
+ " * [How the `object.method()` Syntax Works](#how_the_`object.method()`_syntax_works)\n",
+ " * [Check Your Understanding](#check_your_understanding)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "a66131ab",
"metadata": {},
"source": [
"**Object-oriented programming (OOP)** is a method of structuring a program by bundling related properties and behaviors into individual objects. In this tutorial, you’ll learn the basics of object-oriented programming in Python."
@@ -18,7 +44,6 @@
},
{
"cell_type": "markdown",
- "id": "93eb2af8",
"metadata": {},
"source": [
"Conceptually, objects are like the components of a system. Think of a program as a factory assembly line of sorts. At each step of the assembly line a system component processes some material, ultimately transforming raw material into a finished product."
@@ -26,7 +51,6 @@
},
{
"cell_type": "markdown",
- "id": "2b9cf538",
"metadata": {},
"source": [
"An object contains data, like the raw or preprocessed materials at each step on an assembly line, and behavior, like the action each assembly line component performs."
@@ -34,7 +58,6 @@
},
{
"cell_type": "markdown",
- "id": "7550e2d0",
"metadata": {},
"source": [
"**In this section, you’ll learn how to:**\n",
@@ -46,15 +69,14 @@
},
{
"cell_type": "markdown",
- "id": "3d6d9045",
"metadata": {},
"source": [
- "## What Is Object-Oriented Programming in Python?"
+ "\n",
+ "## What Is Object-Oriented Programming in Python? [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "512f33b1",
"metadata": {},
"source": [
"Object-oriented programming is a programming paradigm that provides a means of structuring programs so that properties and behaviors are bundled into individual **objects**."
@@ -62,7 +84,6 @@
},
{
"cell_type": "markdown",
- "id": "99589313",
"metadata": {},
"source": [
"For instance, an object could represent a person with **properties** like a name, age, and address and **behaviors** such as walking, talking, breathing, and running. Or it could represent an email with properties like a recipient list, subject, and body and behaviors like adding attachments and sending."
@@ -70,7 +91,6 @@
},
{
"cell_type": "markdown",
- "id": "6eac2d69",
"metadata": {},
"source": [
"Put another way, object-oriented programming is an approach for modeling concrete, real-world things, like cars, as well as relations between things, like companies and employees, students and teachers, and so on. OOP models real-world entities as software objects that have some data associated with them and can perform certain functions."
@@ -78,7 +98,6 @@
},
{
"cell_type": "markdown",
- "id": "0e407203",
"metadata": {},
"source": [
"Another common programming paradigm is **procedural programming**, which structures a program like a recipe in that it provides a set of steps, in the form of functions and code blocks, that flow sequentially in order to complete a task."
@@ -86,7 +105,6 @@
},
{
"cell_type": "markdown",
- "id": "789717c7",
"metadata": {},
"source": [
"The key takeaway is that objects are at the center of object-oriented programming in Python, not only representing the data, as in procedural programming, but in the overall structure of the program as well."
@@ -94,15 +112,14 @@
},
{
"cell_type": "markdown",
- "id": "2f9278bf",
"metadata": {},
"source": [
- "## Define a Class in Python"
+ "\n",
+ "## Define a Class in Python [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "8a8c02fc",
"metadata": {},
"source": [
"Primitive data structures—like numbers, strings, and lists—are designed to represent simple pieces of information, such as the cost of an apple, the name of a poem, or your favorite colors, respectively. What if you want to represent something more complex?"
@@ -110,7 +127,6 @@
},
{
"cell_type": "markdown",
- "id": "8abc74a2",
"metadata": {},
"source": [
"For example, let’s say you want to track employees in an organization. You need to store some basic information about each employee, such as their name, age, position, and the year they started working."
@@ -118,7 +134,6 @@
},
{
"cell_type": "markdown",
- "id": "a0e57fdd",
"metadata": {},
"source": [
"One way to do this is to represent each employee as a list:"
@@ -127,7 +142,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "e87ef06b",
"metadata": {},
"outputs": [],
"source": [
@@ -138,7 +152,6 @@
},
{
"cell_type": "markdown",
- "id": "65621fea",
"metadata": {},
"source": [
"There are a number of issues with this approach."
@@ -146,7 +159,6 @@
},
{
"cell_type": "markdown",
- "id": "fce6fb2b",
"metadata": {},
"source": [
"First, it can make larger code files more difficult to manage. If you reference `kirk[0]` several lines away from where the `kirk` list is declared, will you remember that the element with index `0` is the employee’s name?"
@@ -154,7 +166,6 @@
},
{
"cell_type": "markdown",
- "id": "11d5c842",
"metadata": {},
"source": [
"Second, it can introduce errors if not every employee has the same number of elements in the list. In the `mccoy` list above, the age is missing, so `mccoy[1]` will return `\"Chief Medical Officer\"` instead of Dr. McCoy’s age."
@@ -162,7 +173,6 @@
},
{
"cell_type": "markdown",
- "id": "849a5d16",
"metadata": {},
"source": [
"A great way to make this type of code more manageable and more maintainable is to use **classes**."
@@ -170,15 +180,14 @@
},
{
"cell_type": "markdown",
- "id": "1795e5b6",
"metadata": {},
"source": [
- "## Classes vs Instances"
+ "\n",
+ "## Classes vs Instances [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "0804782f",
"metadata": {},
"source": [
"Classes are used to create user-defined data structures. Classes define functions called **methods**, which identify the behaviors and actions that an object created from the class can perform with its data."
@@ -186,7 +195,6 @@
},
{
"cell_type": "markdown",
- "id": "bfb3a31e",
"metadata": {},
"source": [
"In this tutorial, you’ll create a `Dog` class that stores some information about the characteristics and behaviors that an individual dog can have."
@@ -194,7 +202,6 @@
},
{
"cell_type": "markdown",
- "id": "1ef585ae",
"metadata": {},
"source": [
"A class is a blueprint for how something should be defined. It doesn’t necessarily contain any data. The `Dog` class specifies that a name and an age are necessary for defining a dog, but it doesn’t contain the name or age of any specific dog."
@@ -202,7 +209,6 @@
},
{
"cell_type": "markdown",
- "id": "64fcf4f0",
"metadata": {},
"source": [
"While the class is the blueprint, an **instance** is an object that is built from a class and contains real data. An instance of the `Dog` class is not a blueprint anymore. It’s an actual dog with a name, like Miles, who’s four years old."
@@ -210,7 +216,6 @@
},
{
"cell_type": "markdown",
- "id": "33be8500",
"metadata": {},
"source": [
"Put another way, a class is like a form or questionnaire. An instance is like a form that has been filled out with information. Just like many people can fill out the same form with their own unique information, many instances can be created from a single class."
@@ -218,23 +223,14 @@
},
{
"cell_type": "markdown",
- "id": "18693578",
"metadata": {},
"source": [
- "### How to Define a Class"
+ "\n",
+ "### How to Define a Class [
](#table_of_contents)\n"
]
},
- {
- "cell_type": "code",
- "execution_count": 132,
- "id": "f1958129",
- "metadata": {},
- "outputs": [],
- "source": []
- },
{
"cell_type": "markdown",
- "id": "9d02f371",
"metadata": {},
"source": [
"All class definitions start with the `class` keyword, which is followed by the name of the class and a colon. Any code that is indented below the class definition is considered part of the class’s body."
@@ -242,7 +238,6 @@
},
{
"cell_type": "markdown",
- "id": "5655b6d3",
"metadata": {},
"source": [
"Here’s an example of a `Dog` class:\n",
@@ -255,7 +250,6 @@
},
{
"cell_type": "markdown",
- "id": "9ec2f8e8",
"metadata": {},
"source": [
"The body of the `Dog` class consists of a single statement: the `pass` keyword. pass is often used as a placeholder indicating where code will eventually go. It allows you to run this code without Python throwing an error."
@@ -263,7 +257,6 @@
},
{
"cell_type": "markdown",
- "id": "9622d98e",
"metadata": {},
"source": [
"> **Note:** Python class names are written in CapitalizedWords notation (**Pascal** case) by convention. For example, a class for a specific breed of dog like the Jack Russell Terrier would be written as `JackRussellTerrier`."
@@ -271,7 +264,6 @@
},
{
"cell_type": "markdown",
- "id": "ea4175a0",
"metadata": {},
"source": [
"The `Dog` class isn’t very interesting right now, so let’s spruce it up a bit by defining some properties that all `Dog` objects should have. There are a number of properties that we can choose from, including name, age, coat color, and breed. To keep things simple, we’ll just use name and age."
@@ -279,7 +271,6 @@
},
{
"cell_type": "markdown",
- "id": "4f6ded3c",
"metadata": {},
"source": [
"The properties that all `Dog` objects must have are defined in a method called `.__init__()`. Every time a new `Dog` object is created, `.__init__()` sets the initial **state** of the object by assigning the values of the object’s properties. That is, `.__init__()` initializes each new instance of the class."
@@ -287,7 +278,6 @@
},
{
"cell_type": "markdown",
- "id": "0d1d94a9",
"metadata": {},
"source": [
"You can give `.__init__()` any number of parameters, but the first parameter will always be a variable called `self`. When a new class instance is created, the instance is automatically passed to the self parameter in `.__init__()` so that new **attributes** can be defined on the object."
@@ -295,7 +285,6 @@
},
{
"cell_type": "markdown",
- "id": "d9c4b1e1",
"metadata": {},
"source": [
"Let’s update the `Dog` class with an `.__init__()` method that creates `.name` and `.age` attributes:"
@@ -304,7 +293,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "0491faeb",
"metadata": {},
"outputs": [],
"source": [
@@ -316,7 +304,6 @@
},
{
"cell_type": "markdown",
- "id": "796982a1",
"metadata": {},
"source": [
"Notice that the `.__init__()` method’s signature is indented four spaces. The body of the method is indented by eight spaces. This indentation is vitally important. It tells Python that the `.__init__()` method belongs to the Dog class."
@@ -324,7 +311,6 @@
},
{
"cell_type": "markdown",
- "id": "c0c4a849",
"metadata": {},
"source": [
"In the body of `.__init__()`, there are two statements using the self variable:"
@@ -332,7 +318,6 @@
},
{
"cell_type": "markdown",
- "id": "63142457",
"metadata": {},
"source": [
"1. `self.name = name` creates an attribute called `name` and assigns to it the value of the `name` parameter.\n",
@@ -341,7 +326,6 @@
},
{
"cell_type": "markdown",
- "id": "c76c79e1",
"metadata": {},
"source": [
"Attributes created in `.__init__()` are called **instance attributes**. An instance attribute’s value is specific to a particular instance of the class. All `Dog` objects have a `name` and an `age`, but the values for the `name` and `age` attributes will vary depending on the `Dog` instance."
@@ -349,7 +333,6 @@
},
{
"cell_type": "markdown",
- "id": "b72bf746",
"metadata": {},
"source": [
"On the other hand, **class attributes** are attributes that have the same value for all class instances. You can define a class attribute by assigning a value to a variable name outside of `.__init__()`."
@@ -357,7 +340,6 @@
},
{
"cell_type": "markdown",
- "id": "6afe809c",
"metadata": {},
"source": [
"For example, the following `Dog` class has a class attribute called `species` with the value `\"Canis familiaris\"`:"
@@ -366,7 +348,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "d9e5638f",
"metadata": {},
"outputs": [],
"source": [
@@ -381,7 +362,6 @@
},
{
"cell_type": "markdown",
- "id": "2bb7a510",
"metadata": {},
"source": [
"Class attributes are defined directly beneath the first line of the class name and are indented by four spaces. They must always be assigned an initial value. When an instance of the class is created, class attributes are automatically created and assigned to their initial values."
@@ -389,7 +369,6 @@
},
{
"cell_type": "markdown",
- "id": "3f7ac40a",
"metadata": {},
"source": [
"Use class attributes to define properties that should have the same value for every class instance. Use instance attributes for properties that vary from one instance to another."
@@ -397,7 +376,6 @@
},
{
"cell_type": "markdown",
- "id": "80786a2a",
"metadata": {},
"source": [
"Now that we have a `Dog` class, let’s create some dogs!"
@@ -405,16 +383,15 @@
},
{
"cell_type": "markdown",
- "id": "6c88149f",
"metadata": {},
"source": [
- "## Instantiate an Object in Python"
+ "\n",
+ "## Instantiate an Object in Python [
](#table_of_contents)\n"
]
},
{
"cell_type": "code",
"execution_count": 41,
- "id": "b345310c",
"metadata": {},
"outputs": [],
"source": [
@@ -424,7 +401,6 @@
},
{
"cell_type": "markdown",
- "id": "c84b5cf1",
"metadata": {},
"source": [
"This creates a new `Dog` class with no attributes or methods."
@@ -432,7 +408,6 @@
},
{
"cell_type": "markdown",
- "id": "03a4060d",
"metadata": {},
"source": [
"Creating a new object from a class is called **instantiating** an object. You can instantiate a new `Dog` object by typing the name of the class, followed by opening and closing parentheses:"
@@ -441,7 +416,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "e36fc679",
"metadata": {},
"outputs": [
{
@@ -461,7 +435,6 @@
},
{
"cell_type": "markdown",
- "id": "b8bb101d",
"metadata": {},
"source": [
"You now have a new Dog object at `0x7f0b04dcac50`. This funny-looking string of letters and numbers is a **memory address** that indicates where the `Dog` object is stored in your computer’s memory. Note that the address you see on your screen will be different."
@@ -469,7 +442,6 @@
},
{
"cell_type": "markdown",
- "id": "86f5c7fd",
"metadata": {},
"source": [
"Now instantiate a second `Dog` object:"
@@ -478,7 +450,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "b8f9f8e9",
"metadata": {},
"outputs": [
{
@@ -498,7 +469,6 @@
},
{
"cell_type": "markdown",
- "id": "cccfdb02",
"metadata": {},
"source": [
"The new `Dog` instance is located at a different memory address. That’s because it’s an entirely new instance and is completely unique from the first `Dog` object that you instantiated."
@@ -506,7 +476,6 @@
},
{
"cell_type": "markdown",
- "id": "a9dffcc9",
"metadata": {},
"source": [
"To see this another way, type the following:"
@@ -515,7 +484,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "7e450bf3",
"metadata": {},
"outputs": [],
"source": [
@@ -525,7 +493,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "c2b45669",
"metadata": {},
"outputs": [],
"source": [
@@ -535,7 +502,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "b166973f",
"metadata": {},
"outputs": [
{
@@ -555,7 +521,6 @@
},
{
"cell_type": "markdown",
- "id": "d64452e0",
"metadata": {},
"source": [
"In this code, you create two new `Dog` objects and assign them to the variables `a` and `b`. When you compare `a` and `b` using the `==` operator, the result is `False`. Even though `a` and `b` are both instances of the `Dog` class, they represent two distinct objects in memory."
@@ -563,15 +528,14 @@
},
{
"cell_type": "markdown",
- "id": "07f50057",
"metadata": {},
"source": [
- "## Class and Instance Attributes"
+ "\n",
+ "## Class and Instance Attributes [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "2c32c7b4",
"metadata": {},
"source": [
"Now create a new `Dog` class with a class attribute called `.species` and two instance attributes called `.name` and `.age`:"
@@ -580,7 +544,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "825d4c8d",
"metadata": {},
"outputs": [],
"source": [
@@ -593,7 +556,6 @@
},
{
"cell_type": "markdown",
- "id": "bcaf710a",
"metadata": {},
"source": [
"To instantiate objects of this `Dog` class, you need to provide values for the `name` and `age`. If you don’t, then Python raises a `TypeError`:"
@@ -602,7 +564,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "c3ac0859",
"metadata": {},
"outputs": [
{
@@ -623,7 +584,6 @@
},
{
"cell_type": "markdown",
- "id": "8355dc0a",
"metadata": {},
"source": [
"To pass arguments to the `name` and `age` parameters, put values into the parentheses after the class name:"
@@ -632,7 +592,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "f3ea53fc",
"metadata": {},
"outputs": [],
"source": [
@@ -642,7 +601,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "a17acb2a",
"metadata": {},
"outputs": [],
"source": [
@@ -651,7 +609,6 @@
},
{
"cell_type": "markdown",
- "id": "8751f99c",
"metadata": {},
"source": [
"This creates two new `Dog` instances—one for a nine-year-old dog named Buddy and one for a four-year-old dog named Miles."
@@ -659,7 +616,6 @@
},
{
"cell_type": "markdown",
- "id": "555d29c6",
"metadata": {},
"source": [
"The Dog class’s `.__init__()` method has three parameters, so why are only two arguments passed to it in the example?"
@@ -667,7 +623,6 @@
},
{
"cell_type": "markdown",
- "id": "53934ac4",
"metadata": {},
"source": [
"When you instantiate a Dog object, Python creates a new instance and passes it to the first parameter of `.__init__()`. This essentially removes the `self` parameter, so you only need to worry about the `name` and `age` parameters."
@@ -675,7 +630,6 @@
},
{
"cell_type": "markdown",
- "id": "bac6a1cd",
"metadata": {},
"source": [
"After you create the `Dog` instances, you can access their instance attributes using **dot notation**:"
@@ -684,7 +638,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "0b15e28e",
"metadata": {},
"outputs": [
{
@@ -705,7 +658,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "5d261301",
"metadata": {},
"outputs": [
{
@@ -726,7 +678,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "211b9130",
"metadata": {},
"outputs": [
{
@@ -747,7 +698,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "d1a9ce0c",
"metadata": {},
"outputs": [
{
@@ -767,7 +717,6 @@
},
{
"cell_type": "markdown",
- "id": "9c940f21",
"metadata": {},
"source": [
"You can access class attributes the same way:\n",
@@ -777,7 +726,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "3bf9b3b1",
"metadata": {},
"outputs": [
{
@@ -797,7 +745,6 @@
},
{
"cell_type": "markdown",
- "id": "3d7dfd6c",
"metadata": {},
"source": [
"One of the biggest advantages of using classes to organize data is that instances are guaranteed to have the attributes you expect. All `Dog` instances have `.species`, `.name`, and `.age` attributes, so you can use those attributes with confidence knowing that they will always return a value."
@@ -805,7 +752,6 @@
},
{
"cell_type": "markdown",
- "id": "b86f730b",
"metadata": {},
"source": [
"Although the attributes are guaranteed to exist, their values _can_ be changed dynamically:"
@@ -814,7 +760,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "181f2e26",
"metadata": {},
"outputs": [],
"source": [
@@ -824,7 +769,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "29f37588",
"metadata": {},
"outputs": [
{
@@ -845,7 +789,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "d627cc1d",
"metadata": {},
"outputs": [],
"source": [
@@ -855,7 +798,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "62367445",
"metadata": {},
"outputs": [
{
@@ -875,7 +817,6 @@
},
{
"cell_type": "markdown",
- "id": "e2b572a4",
"metadata": {},
"source": [
"In this example, you change the `.age` attribute of the buddy object to `10`. Then you change the `.species` attribute of the `miles` object to `\"Felis silvestris\"`, which is a species of cat. That makes Miles a pretty strange dog, but it is valid Python!"
@@ -883,7 +824,6 @@
},
{
"cell_type": "markdown",
- "id": "0073526f",
"metadata": {},
"source": [
"The key takeaway here is that custom objects are mutable by default. An object is mutable if it can be altered dynamically. For example, lists and dictionaries are mutable, but strings and tuples are immutable."
@@ -891,15 +831,14 @@
},
{
"cell_type": "markdown",
- "id": "a27f5526",
"metadata": {},
"source": [
- "### Instance Methods"
+ "\n",
+ "### Instance Methods [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "a9440ca4",
"metadata": {},
"source": [
"**Instance methods** are functions that are defined inside a class and can only be called from an instance of that class. Just like `.__init__()`, an instance method’s first parameter is always `self`."
@@ -908,7 +847,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "834ab428",
"metadata": {},
"outputs": [],
"source": [
@@ -930,7 +868,6 @@
},
{
"cell_type": "markdown",
- "id": "edd06181",
"metadata": {},
"source": [
"This `Dog` class has two instance methods:\n",
@@ -943,7 +880,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "93afc44d",
"metadata": {},
"outputs": [],
"source": [
@@ -953,7 +889,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "13274fef",
"metadata": {},
"outputs": [
{
@@ -974,7 +909,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "84ce6f78",
"metadata": {},
"outputs": [
{
@@ -995,7 +929,6 @@
{
"cell_type": "code",
"execution_count": 32,
- "id": "b551923b",
"metadata": {},
"outputs": [
{
@@ -1015,7 +948,6 @@
},
{
"cell_type": "markdown",
- "id": "e63dc9d6",
"metadata": {},
"source": [
"In the above Dog class, `.description()` returns a string containing information about the Dog instance `miles`. When writing your own classes, it’s a good idea to have a method that returns a string containing useful information about an instance of the class. However, `.description()` isn’t the most Pythonic way of doing this."
@@ -1023,7 +955,6 @@
},
{
"cell_type": "markdown",
- "id": "e86b1fad",
"metadata": {},
"source": [
"When you create a `list` object, you can use `print()` to display a string that looks like the list:"
@@ -1032,7 +963,6 @@
{
"cell_type": "code",
"execution_count": 33,
- "id": "e9c3f04c",
"metadata": {},
"outputs": [
{
@@ -1050,7 +980,6 @@
},
{
"cell_type": "markdown",
- "id": "8c2b7451",
"metadata": {},
"source": [
"Let’s see what happens when you `print()` the `miles` object:"
@@ -1059,7 +988,6 @@
{
"cell_type": "code",
"execution_count": 34,
- "id": "444b8fc5",
"metadata": {},
"outputs": [
{
@@ -1076,7 +1004,6 @@
},
{
"cell_type": "markdown",
- "id": "a3560053",
"metadata": {},
"source": [
"When you `print(miles)`, you get a cryptic looking message telling you that `miles` is a `Dog` object at the memory address `0x7f0b04d5b050`. This message isn’t very helpful. You can change what gets printed by defining a special instance method called `.__str__()`.\n",
@@ -1094,7 +1021,6 @@
{
"cell_type": "code",
"execution_count": 48,
- "id": "931bc62d",
"metadata": {},
"outputs": [],
"source": [
@@ -1126,7 +1052,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "a0b2f21c",
"metadata": {},
"outputs": [],
"source": [
@@ -1136,7 +1061,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "9adbc965",
"metadata": {},
"outputs": [
{
@@ -1153,7 +1077,6 @@
},
{
"cell_type": "markdown",
- "id": "70a2dc0b",
"metadata": {},
"source": [
"Methods like `.__init__()` and `.__str__()` are called **dunder methods** because they begin and end with double underscores. There are many dunder methods that you can use to customize classes in Python. Although too advanced a topic for a beginning Python tutorial, understanding dunder methods is an important part of mastering object-oriented programming in Python."
@@ -1161,7 +1084,6 @@
},
{
"cell_type": "markdown",
- "id": "9eddb8a2",
"metadata": {},
"source": [
"In the next section, you’ll see how to take your knowledge one step further and create classes from other classes."
@@ -1169,16 +1091,15 @@
},
{
"cell_type": "markdown",
- "id": "f4032b7c",
"metadata": {},
"source": [
- "### How the `object.method()` Syntax Works"
+ "\n",
+ "### How the `object.method()` Syntax Works [
](#table_of_contents)\n"
]
},
{
"cell_type": "code",
"execution_count": 52,
- "id": "7af6a7f7",
"metadata": {},
"outputs": [],
"source": [
@@ -1195,7 +1116,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "28776f43",
"metadata": {},
"outputs": [],
"source": [
@@ -1205,7 +1125,6 @@
{
"cell_type": "code",
"execution_count": 55,
- "id": "27f974d1",
"metadata": {},
"outputs": [
{
@@ -1226,7 +1145,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "a4ff7917",
"metadata": {},
"outputs": [
{
@@ -1246,7 +1164,6 @@
},
{
"cell_type": "markdown",
- "id": "b51e7f84",
"metadata": {},
"source": [
"This example shows how instance is automatically passed to the `self` parameter."
@@ -1254,7 +1171,6 @@
},
{
"cell_type": "markdown",
- "id": "1e4177dc",
"metadata": {},
"source": [
"These two lines are actually identical:\n",
@@ -1269,15 +1185,14 @@
},
{
"cell_type": "markdown",
- "id": "615e0a12",
"metadata": {},
"source": [
- "### Check Your Understanding"
+ "\n",
+ "### Check Your Understanding [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "8d27ab7a",
"metadata": {},
"source": [
"> **Exercise:** Create a `Car` Class\n",
@@ -1296,7 +1211,6 @@
},
{
"cell_type": "markdown",
- "id": "b88803a3",
"metadata": {},
"source": [
"> **Solution:**\n",
@@ -1337,7 +1251,6 @@
},
{
"cell_type": "markdown",
- "id": "4c8faf12",
"metadata": {},
"source": [
"When you’re ready, you can move on to the next section."
@@ -1360,7 +1273,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/02. Object Oriented Programming/03 Inheritance.ipynb b/Python/02. Object Oriented Programming/03 Inheritance.ipynb
similarity index 86%
rename from python/02. Object Oriented Programming/03 Inheritance.ipynb
rename to Python/02. Object Oriented Programming/03 Inheritance.ipynb
index 90f2fcf..a04f2fa 100755
--- a/python/02. Object Oriented Programming/03 Inheritance.ipynb
+++ b/Python/02. Object Oriented Programming/03 Inheritance.ipynb
@@ -4,7 +4,37 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Object Oriented Programming (Part #3: Inheritance)"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Object Oriented Programming (Part 3: Inheritance) \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Analogy](#analogy)\n",
+ "* [Inheritance in Python](#inheritance_in_python)\n",
+ " * [Single Inheritance](#single_inheritance)\n",
+ " * [What is `super()`?](#what_is_`super()`?)\n",
+ " * [Multi-Level Inheritance](#multi-level_inheritance)\n",
+ " * [Hierarchical Inheritance](#hierarchical_inheritance)\n",
+ " * [`issubclass()` & `isinstance()`](#`issubclass()`_&_`isinstance()`)\n",
+ "* [Exceptions Inheritance (Exceptions are an Exception)](#exceptions_inheritance_(exceptions_are_an_exception))\n",
+ "* [Abstract Base Classes](#abstract_base_classes)\n",
+ "* [Check Your Understanding](#check_your_understanding)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
@@ -36,7 +66,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Analogy"
+ "\n",
+ "## Analogy [
](#table_of_contents)\n"
]
},
{
@@ -64,7 +95,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Inheritance in Python"
+ "\n",
+ "## Inheritance in Python [
](#table_of_contents)\n"
]
},
{
@@ -88,7 +120,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Single Inheritance"
+ "\n",
+ "### Single Inheritance [
](#table_of_contents)\n"
]
},
{
@@ -210,7 +243,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### What is `super()`?\n",
+ "\n",
+ "### What is `super()`? [
](#table_of_contents)\n",
+ "\n",
"\n",
"You can access the parent class from inside a method of a child class by using `super()`. When you call `super().method()` inside child class, Python searches the parent class, for a `.method()` method and calls it with the variable sound."
]
@@ -259,7 +294,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Multi-Level Inheritance"
+ "\n",
+ "### Multi-Level Inheritance [
](#table_of_contents)\n"
]
},
{
@@ -313,7 +349,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Hierarchical Inheritance"
+ "\n",
+ "### Hierarchical Inheritance [
](#table_of_contents)\n"
]
},
{
@@ -393,7 +430,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### `issubclass()` & `isinstance()`\n",
+ "\n",
+ "### `issubclass()` & `isinstance()` [
](#table_of_contents)\n",
+ "\n",
"\n",
"Two built-in functions `isinstance()` and `issubclass()` are used to check inheritances. Function `isinstance()` returns True if the object is an instance of the class or other classes derived from it. Each and every class in Python inherits from the base class object.\n",
"\n",
@@ -524,7 +563,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Exceptions Inheritance (Exceptions are an Exception)"
+ "\n",
+ "## Exceptions Inheritance (Exceptions are an Exception) [
](#table_of_contents)\n"
]
},
{
@@ -607,7 +647,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Abstract Base Classes"
+ "\n",
+ "## Abstract Base Classes [
](#table_of_contents)\n"
]
},
{
@@ -714,7 +755,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Check Your Understanding"
+ "\n",
+ "## Check Your Understanding [
](#table_of_contents)\n"
]
},
{
@@ -758,7 +800,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Conclusion"
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n"
]
},
{
@@ -800,7 +843,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/02. Object Oriented Programming/04 Composition.ipynb b/Python/02. Object Oriented Programming/04 Composition.ipynb
similarity index 84%
rename from python/02. Object Oriented Programming/04 Composition.ipynb
rename to Python/02. Object Oriented Programming/04 Composition.ipynb
index 168746d..a93e2be 100755
--- a/python/02. Object Oriented Programming/04 Composition.ipynb
+++ b/Python/02. Object Oriented Programming/04 Composition.ipynb
@@ -2,18 +2,41 @@
"cells": [
{
"cell_type": "markdown",
- "id": "green-microwave",
"metadata": {},
"source": [
- "# OOP (Part #4: Composition)"
+ "
"
]
},
{
"cell_type": "markdown",
- "id": "powered-librarian",
"metadata": {},
"source": [
- "## Implementation Inheritance vs Interface Inheritance\n",
+ "#
OOP (Part 4: Composition) \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Implementation Inheritance vs Interface Inheritance](#implementation_inheritance_vs_interface_inheritance)\n",
+ "* [Composition](#composition)\n",
+ "* [Flexible Designs With Composition](#flexible_designs_with_composition)\n",
+ "* [Choosing Between Inheritance and Composition in Python](#choosing_between_inheritance_and_composition_in_python)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Implementation Inheritance vs Interface Inheritance [
](#table_of_contents)\n",
+ "\n",
"\n",
"When you derive one class from another, the derived class inherits both:\n",
"\n",
@@ -23,7 +46,6 @@
},
{
"cell_type": "markdown",
- "id": "virgin-stations",
"metadata": {},
"source": [
"Most of the time, you’ll want to inherit the implementation of a class, but you will want to implement multiple interfaces, so your objects can be used in different situations. Modern programming languages are designed with this basic concept in mind. They allow you to inherit from a single class, but you can implement multiple interfaces."
@@ -31,7 +53,6 @@
},
{
"cell_type": "markdown",
- "id": "chinese-designation",
"metadata": {},
"source": [
"In Python, you don’t have to explicitly declare an interface. Any object that implements the desired interface can be used in place of another object. This is known as [duck typing](https://realpython.com/python-type-checking/#duck-typing). Duck typing is usually explained as “if it behaves like a duck, then it’s a duck.”"
@@ -39,8 +60,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "id": "incorporate-coupon",
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -67,8 +87,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
- "id": "virtual-margin",
+ "execution_count": 40,
"metadata": {},
"outputs": [],
"source": [
@@ -81,7 +100,7 @@
" self.width = width\n",
" self.height = height\n",
" \n",
- " def area(self,):\n",
+ " def area(self):\n",
" return self.width * self.height\n",
" \n",
" def perimeter(self,):\n",
@@ -100,15 +119,13 @@
},
{
"cell_type": "code",
- "execution_count": 4,
- "id": "shared-nickel",
+ "execution_count": 41,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Shape constructor called!\n",
"Rectangle constructor called!\n"
]
}
@@ -119,7 +136,6 @@
},
{
"cell_type": "markdown",
- "id": "antique-liberty",
"metadata": {},
"source": [
"To illustrate this, you will now add a `Temporary` class to the example above which doesn’t derive from `Shape`:"
@@ -128,7 +144,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "given-hindu",
"metadata": {},
"outputs": [],
"source": [
@@ -143,7 +158,6 @@
},
{
"cell_type": "markdown",
- "id": "corrected-parker",
"metadata": {},
"source": [
"The `Human` class doesn’t derive from `Shape` or `Rectangle`, but it exposes the same interface required by the `.calculate_areas()`. The `Rectangle.calculate_areas()` requires a list of objects that implement the following interface:\n",
@@ -157,7 +171,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "referenced-river",
"metadata": {},
"outputs": [
{
@@ -178,7 +191,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "medium-decision",
"metadata": {},
"outputs": [
{
@@ -206,7 +218,6 @@
},
{
"cell_type": "markdown",
- "id": "optimum-scottish",
"metadata": {},
"source": [
"Since you don’t have to derive from a specific class for your objects to be reusable by the program, you may be asking why you should use inheritance instead of just implementing the desired interface. The following rules may help you:\n",
@@ -218,10 +229,11 @@
},
{
"cell_type": "markdown",
- "id": "respected-terrorist",
"metadata": {},
"source": [
- "## Composition\n",
+ "\n",
+ "## Composition [
](#table_of_contents)\n",
+ "\n",
"\n",
"Composition is an object oriented design concept that models a **has a** relationship. In composition, a class known as **composite** contains an object of another class known to as **component**. In other words, a composite class **has a** component of another class.\n",
"\n",
@@ -230,7 +242,6 @@
},
{
"cell_type": "markdown",
- "id": "technical-jason",
"metadata": {},
"source": [
"The composition relation between two classes is considered **loosely coupled**. That means that changes to the component class rarely affect the composite class, and changes to the composite class never affect the component class. This provides better adaptability to change and allows applications to introduce new requirements without affecting existing code."
@@ -238,7 +249,6 @@
},
{
"cell_type": "markdown",
- "id": "mexican-maryland",
"metadata": {},
"source": [
"For example, an attribute for a shape can be color."
@@ -246,8 +256,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
- "id": "aware-pride",
+ "execution_count": 59,
"metadata": {},
"outputs": [],
"source": [
@@ -263,8 +272,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
- "id": "premium-evaluation",
+ "execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
@@ -273,7 +281,6 @@
},
{
"cell_type": "markdown",
- "id": "through-express",
"metadata": {},
"source": [
"We implemented `__str__()` to provide a pretty representation of an Address. When you `print()` the address variable, the special method `__str__()` is invoked. Since you overloaded the method to return a string formatted as an address, you get a nice, readable representation:"
@@ -281,8 +288,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
- "id": "southeast-radical",
+ "execution_count": 61,
"metadata": {},
"outputs": [
{
@@ -299,7 +305,6 @@
},
{
"cell_type": "markdown",
- "id": "ordinary-jurisdiction",
"metadata": {},
"source": [
"You can now add the `Color` to the `Rectangle` class through composition:"
@@ -307,27 +312,24 @@
},
{
"cell_type": "code",
- "execution_count": 24,
- "id": "annual-radiation",
+ "execution_count": 62,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Shape constructor called!\n",
"Rectangle constructor called!\n"
]
}
],
"source": [
- "r = Rectangle(3, 4, color=red_color)"
+ "r = Rectangle(3, 4, color=Color(\"Red\", \"D33817\", (211, 56, 23)))"
]
},
{
"cell_type": "code",
"execution_count": 25,
- "id": "attended-gathering",
"metadata": {},
"outputs": [
{
@@ -344,7 +346,6 @@
},
{
"cell_type": "markdown",
- "id": "first-level",
"metadata": {},
"source": [
"Composition is a loosely coupled relationship that often doesn’t require the composite class to have knowledge of the component."
@@ -352,7 +353,6 @@
},
{
"cell_type": "markdown",
- "id": "medieval-renewal",
"metadata": {},
"source": [
"The `Rectangle` class leverages the implementation of the `Color` class without any knowledge of what an `Color` object is or how it’s represented. This type of design is so flexible that you can change the `Color` class without any impact to the `Rectangle` class."
@@ -360,10 +360,11 @@
},
{
"cell_type": "markdown",
- "id": "intellectual-granny",
"metadata": {},
"source": [
- "## Flexible Designs With Composition\n",
+ "\n",
+ "## Flexible Designs With Composition [
](#table_of_contents)\n",
+ "\n",
"\n",
"Composition is more flexible than inheritance because it models a loosely coupled relationship. Changes to a component class have minimal or no effects on the composite class. Designs based on composition are more suitable to change.\n",
"\n",
@@ -372,10 +373,11 @@
},
{
"cell_type": "markdown",
- "id": "distinct-cleanup",
"metadata": {},
"source": [
- "## Choosing Between Inheritance and Composition in Python\n",
+ "\n",
+ "## Choosing Between Inheritance and Composition in Python [
](#table_of_contents)\n",
+ "\n",
"\n",
"Python, as an object oriented programming language, supports both inheritance and composition. You saw that inheritance is best used to model an **is a** relationship, whereas composition models a **has a** relationship.\n",
"\n",
@@ -393,6 +395,13 @@
"\n",
"- **Use composition over inheritance in Python** to enable run-time behavior changes without affecting existing classes."
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
@@ -411,7 +420,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/02. Object Oriented Programming/05 Overriding.ipynb b/Python/02. Object Oriented Programming/05 Overriding.ipynb
similarity index 81%
rename from python/02. Object Oriented Programming/05 Overriding.ipynb
rename to Python/02. Object Oriented Programming/05 Overriding.ipynb
index 57b5ce6..b8c4dca 100755
--- a/python/02. Object Oriented Programming/05 Overriding.ipynb
+++ b/Python/02. Object Oriented Programming/05 Overriding.ipynb
@@ -2,15 +2,42 @@
"cells": [
{
"cell_type": "markdown",
- "id": "novel-graduate",
"metadata": {},
"source": [
- "# OOP: Part #5: Overriding"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
OOP: Part 5: Overriding \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [Python Data Model](#python_data_model)\n",
+ "* [Overriding Built-in Functions](#overriding_built-in_functions)\n",
+ " * [Giving a Length to Your Objects Using `len()`](#giving_a_length_to_your_objects_using_`len()`)\n",
+ " * [Making Your Objects Work With `abs()`](#making_your_objects_work_with_`abs()`)\n",
+ " * [Printing Your Objects Prettily Using `str()`](#printing_your_objects_prettily_using_`str()`)\n",
+ " * [Representing Your Objects Using `repr()`](#representing_your_objects_using_`repr()`)\n",
+ " * [Making Your Objects Truthy or Falsey Using `bool()`](#making_your_objects_truthy_or_falsey_using_`bool()`)\n",
+ "* [Overriding Built-in Operators](#overriding_built-in_operators)\n",
+ " * [Making Your Objects Capable of Being Added Using `+`](#making_your_objects_capable_of_being_added_using_`+`)\n",
+ " * [Indexing and Slicing Your Objects Using `[]`](#indexing_and_slicing_your_objects_using_`[]`)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "tribal-madonna",
"metadata": {},
"source": [
"If you’ve used the `+` or `*` operator on a `str` object in Python, you must have noticed its different behavior when compared to `int` or `float` objects:"
@@ -19,7 +46,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "muslim-pregnancy",
"metadata": {},
"outputs": [
{
@@ -40,7 +66,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "missing-looking",
"metadata": {},
"outputs": [
{
@@ -61,7 +86,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "chubby-cambodia",
"metadata": {},
"outputs": [
{
@@ -82,7 +106,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "democratic-sentence",
"metadata": {},
"outputs": [
{
@@ -102,7 +125,6 @@
},
{
"cell_type": "markdown",
- "id": "delayed-lancaster",
"metadata": {},
"source": [
"You might have wondered how the same built-in operator or function shows different behavior for objects of different classes. This is called operator overriding or function overriding, respectively."
@@ -110,15 +132,14 @@
},
{
"cell_type": "markdown",
- "id": "satellite-drilling",
"metadata": {},
"source": [
- "## Python Data Model"
+ "\n",
+ "## Python Data Model [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "usual-terrace",
"metadata": {},
"source": [
"Say you have a class representing an online order having a cart (a list) and a customer (a str or instance of another class which represents a customer)."
@@ -126,7 +147,6 @@
},
{
"cell_type": "markdown",
- "id": "connected-blogger",
"metadata": {},
"source": [
"In such a case, it is quite natural to want to obtain the length of the cart list. Someone new to Python might decide to implement a method called `get_cart_len()` in their class to do this. But you can configure the built-in `len()` in such a way that it returns the length of the cart list when given our object."
@@ -134,7 +154,6 @@
},
{
"cell_type": "markdown",
- "id": "informative-frontier",
"metadata": {},
"source": [
"In another case, we might want to append something to the cart. Again, someone new to Python would think of implementing a method called `append_to_cart()` that takes an item and appends it to the cart list. But you can configure the `+` operator in such a way that it appends a new item to the cart."
@@ -142,7 +161,6 @@
},
{
"cell_type": "markdown",
- "id": "consolidated-tomorrow",
"metadata": {},
"source": [
"Python does all this using special methods. These special methods have a naming convention, where the name starts with two underscores, followed by an identifier and ends with another pair of underscores."
@@ -150,7 +168,6 @@
},
{
"cell_type": "markdown",
- "id": "danish-timothy",
"metadata": {},
"source": [
"Essentially, each built-in function or operator has a special method corresponding to it. For example, there’s `__len__()`, corresponding to `len()`, and `__add__()`, corresponding to the `+` operator."
@@ -158,7 +175,6 @@
},
{
"cell_type": "markdown",
- "id": "bronze-subscription",
"metadata": {},
"source": [
"By default, most of the built-ins and operators will not work with objects of your classes. You must add the corresponding special methods in your class definition to make your object compatible with built-ins and operators. When you do this, the behavior of the function or operator associated with it changes according to that defined in the method."
@@ -166,7 +182,6 @@
},
{
"cell_type": "markdown",
- "id": "elder-wallpaper",
"metadata": {},
"source": [
"This is exactly what the [Data Model (Section 3 of the Python documentation)](https://docs.python.org/3/reference/datamodel.html) helps you accomplish. It lists all the special methods available and provides you with the means of overriding built-in functions and operators so that you can use them on your own objects."
@@ -174,7 +189,6 @@
},
{
"cell_type": "markdown",
- "id": "authorized-florist",
"metadata": {},
"source": [
"**Fun fact:** Due to the naming convention used for these methods, they are also called **dunder methods** which is a shorthand for **d**ouble **under**score methods. Sometimes they’re also referred to as special methods or magic methods. We prefer dunder methods though!"
@@ -182,28 +196,29 @@
},
{
"cell_type": "markdown",
- "id": "tired-ceremony",
"metadata": {},
"source": [
- "## Overriding Built-in Functions\n",
+ "\n",
+ "## Overriding Built-in Functions [
](#table_of_contents)\n",
+ "\n",
"\n",
"Many of the special methods defined in the Data Model can be used to change the behavior of functions such as `len`, `abs`, `hash`, `divmod`, and so on. To do this, you only need to define the corresponding special method in your class. Let’s look at a few examples:"
]
},
{
"cell_type": "markdown",
- "id": "accredited-current",
"metadata": {},
"source": [
- "### Giving a Length to Your Objects Using `len()`\n",
+ "\n",
+ "### Giving a Length to Your Objects Using `len()` [
](#table_of_contents)\n",
+ "\n",
"\n",
"To change the behavior of `len()`, you need to define the `__len__()` special method in your class. Whenever you pass an object of your class to `len()`, your custom definition of `__len__()` will be used to obtain the result. Let’s implement `len()` for the order class we talked about in the beginning:"
]
},
{
"cell_type": "code",
- "execution_count": 21,
- "id": "satisfied-portugal",
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -211,15 +226,14 @@
" def __init__(self, cart, customer):\n",
" self.cart = list(cart)\n",
" self.customer = customer\n",
- "\n",
+ " \n",
" def __len__(self):\n",
" return len(self.cart)"
]
},
{
"cell_type": "code",
- "execution_count": 22,
- "id": "registered-plaza",
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
@@ -228,7 +242,7 @@
"3"
]
},
- "execution_count": 22,
+ "execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
@@ -240,7 +254,6 @@
},
{
"cell_type": "markdown",
- "id": "casual-monte",
"metadata": {},
"source": [
"As you can see, you can now use `len()` to directly obtain the length of the cart. Moreover, it makes more intuitive sense to say “length of order” rather than calling something like `order.get_cart_len()`. Your call is both Pythonic and more intuitive. When you don’t have the `__len__()` method defined but still call `len()` on your object, you get a `TypeError`:"
@@ -249,7 +262,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "conservative-indiana",
"metadata": {},
"outputs": [
{
@@ -259,7 +271,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0morder\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mOrder\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'banana'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'apple'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'mango'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'Real Python'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Calling len when no __len__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_10963/3617644781.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0morder\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mOrder\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'banana'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'apple'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'mango'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'Real Python'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Calling len when no __len__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m: object of type 'Order' has no len()"
]
}
@@ -276,7 +288,6 @@
},
{
"cell_type": "markdown",
- "id": "complicated-tuning",
"metadata": {},
"source": [
"But, when overriding `len()`, you should keep in mind that Python requires the function to return an integer. If your method were to return anything other than an integer, you would get a `TypeError`. This, most probably, is to keep it consistent with the fact that `len()` is generally used to obtain the length of a sequence, which can only be an integer:"
@@ -285,7 +296,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "controlled-stomach",
"metadata": {},
"outputs": [
{
@@ -316,7 +326,6 @@
{
"cell_type": "code",
"execution_count": 32,
- "id": "similar-criterion",
"metadata": {},
"outputs": [],
"source": [
@@ -332,7 +341,6 @@
{
"cell_type": "code",
"execution_count": 33,
- "id": "affected-bedroom",
"metadata": {},
"outputs": [
{
@@ -353,18 +361,18 @@
},
{
"cell_type": "markdown",
- "id": "swedish-madness",
"metadata": {},
"source": [
- "### Making Your Objects Work With `abs()`\n",
+ "\n",
+ "### Making Your Objects Work With `abs()` [
](#table_of_contents)\n",
+ "\n",
"\n",
"You can dictate the behavior of the abs() built-in for instances of your class by defining the `__abs__()` special method in the class."
]
},
{
"cell_type": "code",
- "execution_count": 42,
- "id": "driving-harvey",
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@@ -379,8 +387,7 @@
},
{
"cell_type": "code",
- "execution_count": 43,
- "id": "rolled-garage",
+ "execution_count": 5,
"metadata": {},
"outputs": [
{
@@ -389,7 +396,7 @@
"5.0"
]
},
- "execution_count": 43,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@@ -401,7 +408,6 @@
},
{
"cell_type": "markdown",
- "id": "wired-browser",
"metadata": {},
"source": [
"It makes more intuitive sense to say “absolute value of vector” rather than calling something like `vector.get_mag()`."
@@ -409,15 +415,14 @@
},
{
"cell_type": "markdown",
- "id": "found-thanks",
"metadata": {},
"source": [
- "### Printing Your Objects Prettily Using `str()`"
+ "\n",
+ "### Printing Your Objects Prettily Using `str()` [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "cross-iraqi",
"metadata": {},
"source": [
"The `str()` built-in is used to cast an instance of a class to a str object, or more appropriately, to obtain a user-friendly string representation of the object which can be read by a normal user rather than the programmer. You can define the string format your object should be displayed in when passed to `str()` by defining the `__str__()` method in your class. Moreover, `__str__()` is the method that is used by Python when you call `print()` on your object."
@@ -426,7 +431,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "fancy-helping",
"metadata": {},
"outputs": [],
"source": [
@@ -444,7 +448,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "executive-chapel",
"metadata": {},
"outputs": [
{
@@ -466,7 +469,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "rational-desktop",
"metadata": {},
"outputs": [
{
@@ -483,15 +485,14 @@
},
{
"cell_type": "markdown",
- "id": "supreme-membrane",
"metadata": {},
"source": [
- "### Representing Your Objects Using `repr()`"
+ "\n",
+ "### Representing Your Objects Using `repr()` [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "palestinian-binding",
"metadata": {},
"source": [
"Read [Python String Conversion 101: Why Every Class Needs a “repr”](https://dbader.org/blog/python-repr-vs-str) for a complete tutorial on `__repr__` and the difference between `__repr__` and `__str__`."
@@ -499,7 +500,6 @@
},
{
"cell_type": "markdown",
- "id": "special-privilege",
"metadata": {},
"source": [
"[“Difference between `__str__` and `__repr__` in Python”](https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr) is a Stack Overflow question with excellent contributions from Pythonistas Alex Martelli and Martijn Pieters."
@@ -507,7 +507,6 @@
},
{
"cell_type": "markdown",
- "id": "apparent-beatles",
"metadata": {},
"source": [
"Rule of thumb: `__repr__` is for developers, `__str__` is for customers."
@@ -515,15 +514,14 @@
},
{
"cell_type": "markdown",
- "id": "super-holocaust",
"metadata": {},
"source": [
- "### Making Your Objects Truthy or Falsey Using `bool()`"
+ "\n",
+ "### Making Your Objects Truthy or Falsey Using `bool()` [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "through-litigation",
"metadata": {},
"source": [
"The `bool()` built-in can be used to obtain the truth value of an object. To define its behavior, you can use the `__bool__()`."
@@ -531,7 +529,6 @@
},
{
"cell_type": "markdown",
- "id": "ignored-smooth",
"metadata": {},
"source": [
"The behavior defined here will determine the truth value of an instance in all contexts that require obtaining a truth value such as in `if` statements."
@@ -540,7 +537,6 @@
{
"cell_type": "code",
"execution_count": 121,
- "id": "conceptual-words",
"metadata": {},
"outputs": [],
"source": [
@@ -556,7 +552,6 @@
{
"cell_type": "code",
"execution_count": 122,
- "id": "naval-connectivity",
"metadata": {},
"outputs": [
{
@@ -580,7 +575,6 @@
{
"cell_type": "code",
"execution_count": 123,
- "id": "sharing-gross",
"metadata": {},
"outputs": [
{
@@ -602,7 +596,6 @@
},
{
"cell_type": "markdown",
- "id": "departmental-needle",
"metadata": {},
"source": [
"**Note:** When the `__bool__()` special method is not implemented in a class, the value returned by `__len__()` is used as the truth value, where a non-zero value indicates True and a zero value indicates `False`. In case both the methods are not implemented, all instances of the class are considered to be `True`."
@@ -610,15 +603,14 @@
},
{
"cell_type": "markdown",
- "id": "tutorial-theory",
"metadata": {},
"source": [
- "## Overriding Built-in Operators"
+ "\n",
+ "## Overriding Built-in Operators [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "contemporary-investigation",
"metadata": {},
"source": [
"Changing the behavior of operators is just as simple as changing the behavior of functions. You define their corresponding special methods in your class, and the operators work according to the behavior defined in these methods.\n",
@@ -628,15 +620,14 @@
},
{
"cell_type": "markdown",
- "id": "saving-defensive",
"metadata": {},
"source": [
- "### Making Your Objects Capable of Being Added Using `+`"
+ "\n",
+ "### Making Your Objects Capable of Being Added Using `+` [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "collaborative-letter",
"metadata": {},
"source": [
"The special method corresponding to the `+` operator is the `__add__()` method. Adding a custom definition of `__add__()` changes the behavior of the operator. It is recommended that `__add__()` returns a new instance of the class instead of modifying the calling instance itself."
@@ -645,7 +636,6 @@
{
"cell_type": "code",
"execution_count": 163,
- "id": "passing-diameter",
"metadata": {},
"outputs": [],
"source": [
@@ -663,7 +653,6 @@
{
"cell_type": "code",
"execution_count": 165,
- "id": "strategic-valley",
"metadata": {},
"outputs": [],
"source": [
@@ -673,7 +662,6 @@
{
"cell_type": "code",
"execution_count": 167,
- "id": "designing-irish",
"metadata": {},
"outputs": [
{
@@ -694,7 +682,6 @@
{
"cell_type": "code",
"execution_count": 168,
- "id": "higher-composer",
"metadata": {},
"outputs": [
{
@@ -715,7 +702,6 @@
{
"cell_type": "code",
"execution_count": 169,
- "id": "pleased-greene",
"metadata": {},
"outputs": [
{
@@ -736,7 +722,6 @@
},
{
"cell_type": "markdown",
- "id": "muslim-thriller",
"metadata": {},
"source": [
"Similarly, you have the `__sub__()`, `__mul__()`, and other special methods which define the behavior of `-`, `*`, and so on. These methods should return a new instance of the class as well."
@@ -744,15 +729,14 @@
},
{
"cell_type": "markdown",
- "id": "compliant-exploration",
"metadata": {},
"source": [
- "### Indexing and Slicing Your Objects Using `[]`"
+ "\n",
+ "### Indexing and Slicing Your Objects Using `[]` [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "working-promise",
"metadata": {},
"source": [
"The `[]` operator is called the indexing operator and is used in various contexts in Python such as getting the value at an index in sequences, getting the value associated with a key in dictionaries, or obtaining a part of a sequence through slicing. You can change its behavior using the `__getitem__()` special method."
@@ -760,8 +744,7 @@
},
{
"cell_type": "code",
- "execution_count": 174,
- "id": "blessed-norwegian",
+ "execution_count": 218,
"metadata": {},
"outputs": [],
"source": [
@@ -777,7 +760,6 @@
{
"cell_type": "code",
"execution_count": 176,
- "id": "owned-orange",
"metadata": {},
"outputs": [
{
@@ -799,7 +781,6 @@
{
"cell_type": "code",
"execution_count": 177,
- "id": "lovely-tissue",
"metadata": {},
"outputs": [
{
@@ -820,7 +801,6 @@
{
"cell_type": "code",
"execution_count": 178,
- "id": "surrounded-fortune",
"metadata": {},
"outputs": [
{
@@ -838,9 +818,17 @@
"order[:2]"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 219,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "o = Order([], \"\")"
+ ]
+ },
{
"cell_type": "markdown",
- "id": "recent-johnson",
"metadata": {},
"source": [
"You’ll notice that above, the name of the argument to `__getitem__()` is not index but key. This is because the argument can be of mainly three forms:\n",
@@ -852,7 +840,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "further-gross",
"metadata": {},
"outputs": [
{
@@ -872,7 +859,6 @@
},
{
"cell_type": "markdown",
- "id": "bound-browse",
"metadata": {},
"source": [
"**Note:** There is a similar `__setitem__()` special method that is used to define the behavior of `obj[x] = y`. This method takes two arguments in addition to self, generally called key and value, and can be used to change the value at key to value."
@@ -880,7 +866,6 @@
},
{
"cell_type": "markdown",
- "id": "incredible-concord",
"metadata": {},
"source": [
"[Read More](https://realpython.com/operator-function-overloading/#the-python-data-model)"
@@ -903,7 +888,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/02. Object Oriented Programming/06 Data Types Methods.ipynb b/Python/02. Object Oriented Programming/06 Data Types Methods.ipynb
similarity index 95%
rename from python/02. Object Oriented Programming/06 Data Types Methods.ipynb
rename to Python/02. Object Oriented Programming/06 Data Types Methods.ipynb
index 7bdcc54..b6f6912 100755
--- a/python/02. Object Oriented Programming/06 Data Types Methods.ipynb
+++ b/Python/02. Object Oriented Programming/06 Data Types Methods.ipynb
@@ -4,7 +4,41 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Data Types Methods"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Data Types Methods \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [`int`, `float`, and `complex`](#`int`,_`float`,_and_`complex`)\n",
+ "* [Built-in String Methods](#built-in_string_methods)\n",
+ " * [Case Conversion](#case_conversion)\n",
+ " * [Find and Replace](#find_and_replace)\n",
+ " * [Character Classification](#character_classification)\n",
+ " * [String Formatting](#string_formatting)\n",
+ " * [Converting Between Strings and Lists](#converting_between_strings_and_lists)\n",
+ "* [List Methods](#list_methods)\n",
+ " * [Methods That Modify a List](#methods_that_modify_a_list)\n",
+ "* [`tuple` Methods](#`tuple`_methods)\n",
+ "* [Dictionary Methods](#dictionary_methods)\n",
+ "* [Set Operators and Methods](#set_operators_and_methods)\n",
+ " * [Modifying a Set](#modifying_a_set)\n",
+ " * [Augmented Assignment Operators and Methods](#augmented_assignment_operators_and_methods)\n",
+ " * [Other Methods For Modifying Sets](#other_methods_for_modifying_sets)\n",
+ "\n",
+ "---"
]
},
{
@@ -64,7 +98,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## `int`, `float`, and `complex`"
+ "\n",
+ "## `int`, `float`, and `complex` [
](#table_of_contents)\n"
]
},
{
@@ -81,15 +116,6 @@
"`is_integer()` checks if a float number is actually an integer."
]
},
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "float_num = 1.0"
- ]
- },
{
"cell_type": "code",
"execution_count": 29,
@@ -119,7 +145,7 @@
},
{
"cell_type": "code",
- "execution_count": 30,
+ "execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -128,7 +154,7 @@
},
{
"cell_type": "code",
- "execution_count": 32,
+ "execution_count": 4,
"metadata": {},
"outputs": [
{
@@ -137,7 +163,7 @@
"(1-3j)"
]
},
- "execution_count": 32,
+ "execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
@@ -155,36 +181,38 @@
},
{
"cell_type": "code",
- "execution_count": 33,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "(1.0, 0.0)"
+ "(1.0, 3.0)"
]
},
- "execution_count": 33,
+ "execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "x.real, x.imag"
+ "complex_num.real, complex_num.imag"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Built-in String Methods"
+ "\n",
+ "## Built-in String Methods [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Case Conversion"
+ "\n",
+ "### Case Conversion [
](#table_of_contents)\n"
]
},
{
@@ -434,7 +462,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Find and Replace"
+ "\n",
+ "### Find and Replace [
](#table_of_contents)\n"
]
},
{
@@ -689,16 +718,16 @@
},
{
"cell_type": "code",
- "execution_count": 51,
+ "execution_count": 45,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "8"
+ "0"
]
},
- "execution_count": 51,
+ "execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
@@ -709,7 +738,7 @@
},
{
"cell_type": "code",
- "execution_count": 53,
+ "execution_count": 44,
"metadata": {},
"outputs": [
{
@@ -718,7 +747,7 @@
"-1"
]
},
- "execution_count": 53,
+ "execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
@@ -1018,7 +1047,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Character Classification"
+ "\n",
+ "### Character Classification [
](#table_of_contents)\n"
]
},
{
@@ -1046,7 +1076,7 @@
},
{
"cell_type": "code",
- "execution_count": 64,
+ "execution_count": 46,
"metadata": {},
"outputs": [
{
@@ -1055,7 +1085,7 @@
"True"
]
},
- "execution_count": 64,
+ "execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
@@ -1086,7 +1116,7 @@
},
{
"cell_type": "code",
- "execution_count": 66,
+ "execution_count": 47,
"metadata": {},
"outputs": [
{
@@ -1095,7 +1125,7 @@
"False"
]
},
- "execution_count": 66,
+ "execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
@@ -1274,16 +1304,16 @@
},
{
"cell_type": "code",
- "execution_count": 74,
+ "execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "False"
+ "True"
]
},
- "execution_count": 74,
+ "execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
@@ -1750,7 +1780,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### String Formatting"
+ "\n",
+ "### String Formatting [
](#table_of_contents)\n"
]
},
{
@@ -2596,7 +2627,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Converting Between Strings and Lists"
+ "\n",
+ "### Converting Between Strings and Lists [
](#table_of_contents)\n"
]
},
{
@@ -3366,14 +3398,16 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## List Methods"
+ "\n",
+ "## List Methods [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Methods That Modify a List"
+ "\n",
+ "### Methods That Modify a List [
](#table_of_contents)\n"
]
},
{
@@ -3413,6 +3447,44 @@
"`a.append()` appends object `` to the end of list `a`:"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = [1, 2, 3]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x.append(3)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[1, 2, 3, 3]"
+ ]
+ },
+ "execution_count": 66,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "x"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 148,
@@ -3708,7 +3780,7 @@
},
{
"cell_type": "code",
- "execution_count": 165,
+ "execution_count": 100,
"metadata": {},
"outputs": [],
"source": [
@@ -3718,7 +3790,7 @@
},
{
"cell_type": "code",
- "execution_count": 166,
+ "execution_count": 101,
"metadata": {},
"outputs": [
{
@@ -3727,7 +3799,7 @@
"3.14159"
]
},
- "execution_count": 166,
+ "execution_count": 101,
"metadata": {},
"output_type": "execute_result"
}
@@ -3738,7 +3810,7 @@
},
{
"cell_type": "code",
- "execution_count": 167,
+ "execution_count": 102,
"metadata": {},
"outputs": [
{
@@ -3747,7 +3819,7 @@
"['foo', 'bar', 'baz', 3.14159, 'qux', 'quux', 'corge']"
]
},
- "execution_count": 167,
+ "execution_count": 102,
"metadata": {},
"output_type": "execute_result"
}
@@ -4045,7 +4117,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## `tuple` Methods"
+ "\n",
+ "## `tuple` Methods [
](#table_of_contents)\n"
]
},
{
@@ -4066,7 +4139,7 @@
},
{
"cell_type": "code",
- "execution_count": 188,
+ "execution_count": 118,
"metadata": {},
"outputs": [],
"source": [
@@ -4075,7 +4148,7 @@
},
{
"cell_type": "code",
- "execution_count": 189,
+ "execution_count": 119,
"metadata": {},
"outputs": [
{
@@ -4084,7 +4157,7 @@
"3"
]
},
- "execution_count": 189,
+ "execution_count": 119,
"metadata": {},
"output_type": "execute_result"
}
@@ -4095,7 +4168,7 @@
},
{
"cell_type": "code",
- "execution_count": 190,
+ "execution_count": 120,
"metadata": {},
"outputs": [
{
@@ -4104,7 +4177,7 @@
"2"
]
},
- "execution_count": 190,
+ "execution_count": 120,
"metadata": {},
"output_type": "execute_result"
}
@@ -4124,7 +4197,7 @@
},
{
"cell_type": "code",
- "execution_count": 195,
+ "execution_count": 121,
"metadata": {},
"outputs": [],
"source": [
@@ -4133,7 +4206,7 @@
},
{
"cell_type": "code",
- "execution_count": 196,
+ "execution_count": 122,
"metadata": {},
"outputs": [
{
@@ -4142,7 +4215,7 @@
"2"
]
},
- "execution_count": 196,
+ "execution_count": 122,
"metadata": {},
"output_type": "execute_result"
}
@@ -4153,7 +4226,7 @@
},
{
"cell_type": "code",
- "execution_count": 199,
+ "execution_count": 123,
"metadata": {},
"outputs": [
{
@@ -4163,7 +4236,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Bark!'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Bark!'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mValueError\u001b[0m: 'Bark!' is not in list"
]
}
@@ -4176,7 +4249,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Dictionary Methods"
+ "\n",
+ "## Dictionary Methods [
](#table_of_contents)\n"
]
},
{
@@ -4211,7 +4285,7 @@
},
{
"cell_type": "code",
- "execution_count": 200,
+ "execution_count": 124,
"metadata": {},
"outputs": [],
"source": [
@@ -4220,7 +4294,7 @@
},
{
"cell_type": "code",
- "execution_count": 201,
+ "execution_count": 125,
"metadata": {},
"outputs": [
{
@@ -4229,7 +4303,7 @@
"{'a': 10, 'b': 20, 'c': 30}"
]
},
- "execution_count": 201,
+ "execution_count": 125,
"metadata": {},
"output_type": "execute_result"
}
@@ -4240,7 +4314,7 @@
},
{
"cell_type": "code",
- "execution_count": 202,
+ "execution_count": 126,
"metadata": {},
"outputs": [],
"source": [
@@ -4249,7 +4323,7 @@
},
{
"cell_type": "code",
- "execution_count": 203,
+ "execution_count": 127,
"metadata": {},
"outputs": [
{
@@ -4258,7 +4332,7 @@
"{}"
]
},
- "execution_count": 203,
+ "execution_count": 127,
"metadata": {},
"output_type": "execute_result"
}
@@ -4641,7 +4715,7 @@
},
{
"cell_type": "code",
- "execution_count": 218,
+ "execution_count": 152,
"metadata": {},
"outputs": [],
"source": [
@@ -4650,7 +4724,7 @@
},
{
"cell_type": "code",
- "execution_count": 219,
+ "execution_count": 153,
"metadata": {},
"outputs": [
{
@@ -4659,7 +4733,7 @@
"20"
]
},
- "execution_count": 219,
+ "execution_count": 153,
"metadata": {},
"output_type": "execute_result"
}
@@ -4670,7 +4744,7 @@
},
{
"cell_type": "code",
- "execution_count": 220,
+ "execution_count": 154,
"metadata": {},
"outputs": [
{
@@ -4679,7 +4753,7 @@
"{'a': 10, 'c': 30}"
]
},
- "execution_count": 220,
+ "execution_count": 154,
"metadata": {},
"output_type": "execute_result"
}
@@ -5004,7 +5078,7 @@
},
{
"cell_type": "code",
- "execution_count": 236,
+ "execution_count": 160,
"metadata": {},
"outputs": [],
"source": [
@@ -5013,7 +5087,7 @@
},
{
"cell_type": "code",
- "execution_count": 237,
+ "execution_count": 161,
"metadata": {},
"outputs": [],
"source": [
@@ -5022,7 +5096,7 @@
},
{
"cell_type": "code",
- "execution_count": 238,
+ "execution_count": 162,
"metadata": {},
"outputs": [
{
@@ -5031,7 +5105,7 @@
"{'a': 10, 'b': 200, 'c': 30, 'd': 400}"
]
},
- "execution_count": 238,
+ "execution_count": 162,
"metadata": {},
"output_type": "execute_result"
}
@@ -5050,7 +5124,7 @@
},
{
"cell_type": "code",
- "execution_count": 239,
+ "execution_count": 163,
"metadata": {},
"outputs": [],
"source": [
@@ -5059,7 +5133,7 @@
},
{
"cell_type": "code",
- "execution_count": 240,
+ "execution_count": 164,
"metadata": {},
"outputs": [],
"source": [
@@ -5068,7 +5142,7 @@
},
{
"cell_type": "code",
- "execution_count": 241,
+ "execution_count": 165,
"metadata": {},
"outputs": [
{
@@ -5077,7 +5151,7 @@
"{'a': 10, 'b': 200, 'c': 30, 'd': 400}"
]
},
- "execution_count": 241,
+ "execution_count": 165,
"metadata": {},
"output_type": "execute_result"
}
@@ -5090,7 +5164,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Set Operators and Methods"
+ "\n",
+ "## Set Operators and Methods [
](#table_of_contents)\n"
]
},
{
@@ -5184,7 +5259,7 @@
},
{
"cell_type": "code",
- "execution_count": 245,
+ "execution_count": 175,
"metadata": {},
"outputs": [],
"source": [
@@ -5196,7 +5271,7 @@
},
{
"cell_type": "code",
- "execution_count": 246,
+ "execution_count": 179,
"metadata": {},
"outputs": [
{
@@ -5205,7 +5280,7 @@
"{1, 2, 3, 4, 5, 6, 7}"
]
},
- "execution_count": 246,
+ "execution_count": 179,
"metadata": {},
"output_type": "execute_result"
}
@@ -5216,7 +5291,7 @@
},
{
"cell_type": "code",
- "execution_count": 247,
+ "execution_count": 182,
"metadata": {},
"outputs": [
{
@@ -5225,7 +5300,7 @@
"{1, 2, 3, 4, 5, 6, 7}"
]
},
- "execution_count": 247,
+ "execution_count": 182,
"metadata": {},
"output_type": "execute_result"
}
@@ -6385,7 +6460,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Modifying a Set"
+ "\n",
+ "### Modifying a Set [
](#table_of_contents)\n"
]
},
{
@@ -6399,7 +6475,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Augmented Assignment Operators and Methods\n",
+ "\n",
+ "#### Augmented Assignment Operators and Methods [
](#table_of_contents)\n",
+ "\n",
+ "\n",
"Each of the union, intersection, difference, and symmetric difference operators listed above has an augmented assignment form that can be used to modify a set. For each, there is a corresponding method as well."
]
},
@@ -6687,7 +6766,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Other Methods For Modifying Sets"
+ "\n",
+ "### Other Methods For Modifying Sets [
](#table_of_contents)\n"
]
},
{
@@ -6828,7 +6908,7 @@
},
{
"cell_type": "code",
- "execution_count": 317,
+ "execution_count": 195,
"metadata": {},
"outputs": [],
"source": [
@@ -6837,7 +6917,7 @@
},
{
"cell_type": "code",
- "execution_count": 318,
+ "execution_count": 196,
"metadata": {},
"outputs": [
{
@@ -6846,7 +6926,7 @@
"{'bar', 'foo'}"
]
},
- "execution_count": 318,
+ "execution_count": 196,
"metadata": {},
"output_type": "execute_result"
}
@@ -6858,7 +6938,7 @@
},
{
"cell_type": "code",
- "execution_count": 319,
+ "execution_count": 197,
"metadata": {},
"outputs": [
{
@@ -6867,7 +6947,7 @@
"{'bar', 'foo'}"
]
},
- "execution_count": 319,
+ "execution_count": 197,
"metadata": {},
"output_type": "execute_result"
}
@@ -7118,7 +7198,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/Python/02. Object Oriented Programming/07. Reading and Writing Files.ipynb b/Python/02. Object Oriented Programming/07. Reading and Writing Files.ipynb
new file mode 100644
index 0000000..068d9af
--- /dev/null
+++ b/Python/02. Object Oriented Programming/07. Reading and Writing Files.ipynb
@@ -0,0 +1,1276 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Reading and Writing Files \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [What Is a File?](#what_is_a_file?)\n",
+ " * [File Paths](#file_paths)\n",
+ " * [Line Endings](#line_endings)\n",
+ " * [Character Encodings](#character_encodings)\n",
+ "* [Opening and Closing a File in Python](#opening_and_closing_a_file_in_python)\n",
+ " * [Text File Types](#text_file_types)\n",
+ " * [Buffered Binary File Types](#buffered_binary_file_types)\n",
+ " * [Raw File Types](#raw_file_types)\n",
+ "* [Reading and Writing Opened Files](#reading_and_writing_opened_files)\n",
+ " * [Iterating Over Each Line in the File](#iterating_over_each_line_in_the_file)\n",
+ " * [Appending to a File](#appending_to_a_file)\n",
+ " * [Working With Two Files at the Same Time](#working_with_two_files_at_the_same_time)\n",
+ " * [Working With Bytes](#working_with_bytes)\n",
+ "* [Summary](#summary)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "One of the most common tasks that you can do with Python is reading and writing files. Whether it’s writing to a simple text file, reading a complicated server log, or even analyzing raw byte data, all of these situations require reading or writing a file."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## What Is a File? [
](#table_of_contents)\n",
+ "\n",
+ "\n",
+ "Before we can go into how to work with files in Python, it’s important to understand what exactly a file is and how modern operating systems handle some of their aspects."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "At its core, a file is a contiguous set of bytes [used to store data](https://en.wikipedia.org/wiki/Computer_file). This data is organized in a specific format and can be anything as simple as a text file or as complicated as a program executable. In the end, these byte files are then translated into binary 1 and 0 for easier processing by the computer."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Files on most modern file systems are composed of three main parts:\n",
+ "\n",
+ "- **Header**: metadata about the contents of the file (file name, size, type, and so on)\n",
+ "- **Data**: contents of the file as written by the creator or editor\n",
+ "- **End of file (EOF)**: special character that indicates the end of the file"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What this data represents depends on the format specification used, which is typically represented by an extension. For example, a file that has an extension of `.gif` most likely conforms to the [Graphics Interchange Format](https://en.wikipedia.org/wiki/GIF) specification. There are hundreds, if not thousands, of file extensions out there. For this section, you’ll only deal with `.txt` file extensions."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### File Paths [
](#table_of_contents)\n",
+ "\n",
+ "\n",
+ "When you access a file on an operating system, a file path is required. The file path is a string that represents the location of a file. It’s broken up into three major parts:\n",
+ "\n",
+ "1. **Folder Path:** the file folder location on the file system where subsequent folders are separated by a forward slash `/` (Unix) or backslash `\\` (Windows)\n",
+ "2. **File Name:** the actual name of the file\n",
+ "3. **Extension:** the end of the file path pre-pended with a period (`.`) used to indicate the file type"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s a quick example. Let’s say you have a file located within a file structure like this:\n",
+ "\n",
+ "```\n",
+ "/\n",
+ "│\n",
+ "├── path/\n",
+ "| │\n",
+ "│ ├── to/\n",
+ "│ │ └── cats.gif\n",
+ "│ │\n",
+ "│ └── dog_breeds.txt\n",
+ "|\n",
+ "└── animals.csv\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s say you wanted to access the `cats.gif` file, and your current location was in the same folder as path. In order to access the file, you need to go through the path folder and then the to folder, finally arriving at the `cats.gif` file. The Folder Path is `path/to/`. The File Name is `cats`. The File Extension is `.gif`. So the full path is `path/to/cats.gif`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now let’s say that your current location or current working directory (cwd) is in the to folder of our example folder structure. Instead of referring to the `cats.gif` by the full path of `path/to/cats.gif`, the file can be simply referenced by the file name and extension `cats.gif`.\n",
+ "\n",
+ "```\n",
+ "/\n",
+ "│\n",
+ "├── path/\n",
+ "| │\n",
+ "| ├── to/ ← Your current working directory (cwd) is here\n",
+ "| │ └── cats.gif ← Accessing this file\n",
+ "| │\n",
+ "| └── dog_breeds.txt\n",
+ "|\n",
+ "└── animals.csv\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But what about `dog_breeds.txt`? How would you access that without using the full path? You can use the special characters double-dot (`..`) to move one directory up. This means that `../dog_breeds.txt` will reference the `dog_breeds.txt` file from the directory of to:\n",
+ "\n",
+ "```\n",
+ "/\n",
+ "│\n",
+ "├── path/ ← Referencing this parent folder\n",
+ "| │\n",
+ "| ├── to/ ← Current working directory (cwd)\n",
+ "| │ └── cats.gif\n",
+ "| │\n",
+ "| └── dog_breeds.txt ← Accessing this file\n",
+ "|\n",
+ "└── animals.csv\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The double-dot (`..`) can be chained together to traverse multiple directories above the current directory. For example, to access `animals.csv` from the to folder, you would use `../../animals.csv`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Line Endings [
](#table_of_contents)\n",
+ "\n",
+ "\n",
+ "One problem often encountered when working with file data is the representation of a new line or line ending. The line ending has its roots from back in the Morse Code era, [when a specific pro-sign was used to communicate the end of a transmission or the end of a line](https://en.wikipedia.org/wiki/Prosigns_for_Morse_code#Official_International_Morse_code_procedure_signs)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Later, this [was standardized for teleprinters](https://en.wikipedia.org/wiki/Newline#History) by both the International Organization for Standardization (ISO) and the American Standards Association (ASA). ASA standard states that line endings should use the sequence of the Carriage Return (CR or \\r) and the Line Feed (LF or \\n) characters (CR+LF or \\r\\n). The ISO standard however allowed for either the CR+LF characters or just the LF character."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "[Windows uses the CR+LF characters](https://unix.stackexchange.com/a/411830) to indicate a new line, while Unix and the newer Mac versions use just the LF character. This can cause some complications when you’re processing files on an operating system that is different than the file’s source. Here’s a quick example. Let’s say that we examine the file `dog_breeds.txt` that was created on a Windows system:\n",
+ "\n",
+ "```\n",
+ "Pug\\r\\n\n",
+ "Jack Russell Terrier\\r\\n\n",
+ "English Springer Spaniel\\r\\n\n",
+ "German Shepherd\\r\\n\n",
+ "Staffordshire Bull Terrier\\r\\n\n",
+ "Cavalier King Charles Spaniel\\r\\n\n",
+ "Golden Retriever\\r\\n\n",
+ "West Highland White Terrier\\r\\n\n",
+ "Boxer\\r\\n\n",
+ "Border Terrier\\r\\n\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This same output will be interpreted on a Unix device differently:\n",
+ "\n",
+ "```Pug\\r\n",
+ "\\n\n",
+ "Jack Russell Terrier\\r\n",
+ "\\n\n",
+ "English Springer Spaniel\\r\n",
+ "\\n\n",
+ "German Shepherd\\r\n",
+ "\\n\n",
+ "Staffordshire Bull Terrier\\r\n",
+ "\\n\n",
+ "Cavalier King Charles Spaniel\\r\n",
+ "\\n\n",
+ "Golden Retriever\\r\n",
+ "\\n\n",
+ "West Highland White Terrier\\r\n",
+ "\\n\n",
+ "Boxer\\r\n",
+ "\\n\n",
+ "Border Terrier\\r\n",
+ "\\n\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Character Encodings [
](#table_of_contents)\n",
+ "\n",
+ "\n",
+ "Another common problem that you may face is the encoding of the byte data. An encoding is a translation from byte data to human readable characters. This is typically done by assigning a numerical value to represent a character. The two most common encodings are the [ASCII](https://www.ascii-code.com/) and [UNICODE](https://unicode.org/) Formats. [ASCII can only store 128 characters](https://en.wikipedia.org/wiki/ASCII), while [Unicode can contain up to 1,114,112 characters](https://en.wikipedia.org/wiki/Unicode)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "ASCII is actually a subset of [Unicode](https://realpython.com/python-encodings-guide/) (UTF-8), meaning that ASCII and Unicode share the same numerical to character values. It’s important to note that parsing a file with the incorrect character encoding can lead to failures or misrepresentation of the character. For example, if a file was created using the UTF-8 encoding, and you try to parse it using the ASCII encoding, if there is a character that is outside of those 128 values, then an error will be thrown."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Opening and Closing a File in Python [
](#table_of_contents)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you want to work with a file, the first thing to do is to open it. This is done by invoking the `open()` built-in function. `open()` has a single required argument that is the path to the file. `open()` has a single return, the file object:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Writing myfile.txt\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile myfile.txt\n",
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file = open('myfile.txt')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "After you open a file, the next thing to learn is how to close it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> **Warning:** You should always make sure that an open file is properly closed."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It’s important to remember that it’s your responsibility to close the file. In most cases, upon termination of an application or script, a file will be closed eventually. However, there is no guarantee when exactly that will happen. This can lead to unwanted behavior including resource leaks. It’s also a best practice within Python (Pythonic) to make sure that your code behaves in a way that is well defined and reduces any unwanted behavior."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you’re manipulating a file, there are two ways that you can use to ensure that a file is closed properly, even when encountering an error. The first way to close a file is to use the `try-finally` block:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "reader = open('myfile.txt')\n",
+ "try:\n",
+ " # Further file processing goes here\n",
+ " pass\n",
+ "finally:\n",
+ " reader.close()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> The `try-finally` block is covered in Error Handling section later."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The second way to close a file is to use the `with statement`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with open('myfile.txt') as reader:\n",
+ " # Further file processing goes here\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The with statement automatically takes care of closing the file once it leaves the with block, even in cases of error. I highly recommend that you use the with statement as much as possible, as it allows for cleaner code and makes handling any unexpected errors easier for you."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Most likely, you’ll also want to use the second positional argument, mode. This argument is a string that contains multiple characters to represent how you want to open the file. The default and most common is `'r'`, which represents opening the file in read-only mode as a text file:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " # Further file processing goes here\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Other options for modes are [fully documented online](https://docs.python.org/3/library/functions.html#open), but the most commonly used ones are the following:\n",
+ "\n",
+ "|Character | Meaning|\n",
+ "|:--|:--|\n",
+ "|`'r'` | Open for reading (default)|\n",
+ "|`'w'` | Open for writing, truncating (overwriting) the file first|\n",
+ "|`'rb'` or `'wb'` | Open in binary mode (read/write using byte data)|"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s talk a little about file objects. A file object is:\n",
+ "\n",
+ "> \"an object exposing a file-oriented API (with methods such as read() or write()) to an underlying resource.\" ([Source](https://docs.python.org/3/glossary.html#term-file-object))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are three different categories of file objects:\n",
+ "\n",
+ "- Text files\n",
+ "- Buffered binary files\n",
+ "- Raw binary files"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Each of these file types are defined in the `io` module. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Text File Types [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A text file is the most common file that you’ll encounter. Here are some examples of how these files are opened:\n",
+ "\n",
+ "```python\n",
+ "open('abc.txt')\n",
+ "\n",
+ "open('abc.txt', 'r')\n",
+ "\n",
+ "open('abc.txt', 'w')\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "With these types of files, `open()` will return a `TextIOWrapper` file object:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file = open('myfile.txt')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "_io.TextIOWrapper"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(file)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is the default file object returned by `open()`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Buffered Binary File Types [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A buffered binary file type is used for reading and writing binary files. Here are some examples of how these files are opened:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file = open('myfile.txt', 'rb')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "_io.BufferedReader"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(file)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file = open('myfile.txt', 'wb')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "_io.BufferedWriter"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "type(file)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "With these types of files, `open()` will return either a `BufferedReader` or `BufferedWriter` file object:"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Raw File Types [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A raw file type is:\n",
+ "\n",
+ "> “generally used as a low-level building-block for binary and text streams.” ([Source](https://docs.python.org/3.7/library/io.html#raw-i-o))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It is therefore not typically used."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s an example of how these files are opened:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "<_io.FileIO name='myfile.txt' mode='rb' closefd=True>"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "open('myfile.txt', 'rb', buffering=0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "With these types of files, `open()` will return a `FileIO` file object:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "_io.FileIO"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "file = open('myfile.txt', 'rb', buffering=0)\n",
+ "type(file)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Reading and Writing Opened Files [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Once you’ve opened up a file, you’ll want to read or write to the file. First off, let’s cover reading a file. There are multiple methods that can be called on a file object to help you out:\n",
+ "\n",
+ "|Method |What It Does|\n",
+ "|:--|:--|\n",
+ "|`.read(size=-1)` | This reads from the file based on the number of `size` bytes. If no argument is passed or `None` or `-1` is passed, then the entire file is read. |\n",
+ "|`.readline(size=-1)` | This reads at most size number of characters from the line. This continues to the end of the line and then wraps back around. If no argument is passed or `None` or `-1` is passed, then the entire line (or rest of the line) is read. |\n",
+ "|`.readlines()` | This reads the remaining lines from the file object and returns them as a list. |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using the same `myfile.txt` file you used above, let’s go through some examples of how to use these methods. Here’s an example of how to open and read the entire file using `.read()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Overwriting myfile.txt\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%writefile myfile.txt\n",
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " # Read & print the entire file\n",
+ " print(reader.read())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s an example of how to read 5 bytes of a line each time using the Python `.readline()` method:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "this \n",
+ "is li\n",
+ "ne 1\n",
+ "\n",
+ "this \n",
+ "is li\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " # Read & print the first 5 characters of the line 5 times\n",
+ " print(reader.readline(5))\n",
+ "\n",
+ " # Notice that line is greater than the 5 chars and continues\n",
+ " # down the line, reading 5 chars each time until the end of the\n",
+ " # line and then \"wraps\" around\n",
+ " print(reader.readline(5))\n",
+ " print(reader.readline(5))\n",
+ " print(reader.readline(5))\n",
+ " print(reader.readline(5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s an example of how to read the entire file as a list using the Python `.readlines()` method:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['this is line 1\\n',\n",
+ " 'this is line 2\\n',\n",
+ " 'this is line 3\\n',\n",
+ " 'this is line 4\\n',\n",
+ " 'this is line 5\\n',\n",
+ " 'this is line 6\\n']"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "f = open('myfile.txt')\n",
+ "f.readlines() # Returns a list object"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The above example can also be done by using `list()` to create a list out of the file object:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['this is line 1\\n',\n",
+ " 'this is line 2\\n',\n",
+ " 'this is line 3\\n',\n",
+ " 'this is line 4\\n',\n",
+ " 'this is line 5\\n',\n",
+ " 'this is line 6\\n']"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "f = open('myfile.txt')\n",
+ "list(f)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Iterating Over Each Line in the File [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A common thing to do while reading a file is to iterate over each line. Here’s an example of how to use the Python `.readline()` method to perform that iteration:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " # Read and print the entire file line by line\n",
+ " line = reader.readline()\n",
+ " while line != '': # The EOF char is an empty string\n",
+ " print(line, end='')\n",
+ " line = reader.readline()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Another way you could iterate over each line in the file is to use the Python `.readlines()` method of the file object. Remember, `.readlines()` returns a list where each element in the list represents a line in the file:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " for line in reader.readlines():\n",
+ " print(line, end='')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "However, the above examples can be further simplified by iterating over the file object itself:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " # Read and print the entire file line by line\n",
+ " for line in reader:\n",
+ " print(line, end='')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This final approach is more Pythonic and can be quicker and more memory efficient. Therefore, it is suggested you use this instead."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> Note: Some of the above examples contain `print('some text', end='')`. The `end=''` is to prevent Python from adding an additional newline to the text that is being printed and only print what is being read from the file."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now let’s dive into writing files. As with reading files, file objects have multiple methods that are useful for writing to a file:\n",
+ "\n",
+ "|Method\t|What It Does|\n",
+ "|:--|:--|\n",
+ "|`.write(string)`\t|This writes the string to the file.|\n",
+ "|`.writelines(seq)`\t|This writes the sequence to the file. No line endings are appended to each sequence item. It’s up to you to add the appropriate line ending(s).|"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s a quick example of using `.write()` and `.writelines()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " # Note: readlines doesn't trim the line endings\n",
+ " lines = reader.readlines()\n",
+ "\n",
+ "with open('myfile_reversed.txt', 'w') as writer:\n",
+ " # Alternatively you could use\n",
+ " # writer.writelines(reversed(dog_breeds))\n",
+ "\n",
+ " # Write the dog breeds to the file in reversed order\n",
+ " for line in reversed(lines):\n",
+ " writer.write(line)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with open('myfile_reversed.txt', 'r') as reader:\n",
+ " # Note: readlines doesn't trim the line endings\n",
+ " lines = reader.readlines()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['this is line 6\\n',\n",
+ " 'this is line 5\\n',\n",
+ " 'this is line 4\\n',\n",
+ " 'this is line 3\\n',\n",
+ " 'this is line 2\\n',\n",
+ " 'this is line 1\\n']"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "lines"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Appending to a File [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Sometimes, you may want to append to a file or start writing at the end of an already populated file. This is easily done by using the `'a'` character for the mode argument:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with open('myfile.txt', 'a') as a_writer:\n",
+ " a_writer.write('this is line 7')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you examine `myfile.txt` again, you’ll see that the beginning of the file is unchanged and Beagle is now added to the end of the file:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "this is line 1\n",
+ "this is line 2\n",
+ "this is line 3\n",
+ "this is line 4\n",
+ "this is line 5\n",
+ "this is line 6\n",
+ "this is line 7\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'r') as reader:\n",
+ " print(reader.read())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Working With Two Files at the Same Time [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are times when you may want to read a file and write to another file at the same time. If you use the example that was shown when you were learning how to write to a file, it can actually be combined into the following:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "d_path = 'myfile.txt'\n",
+ "d_r_path = 'myfile_reversed.txt'\n",
+ "with open(d_path, 'r') as reader, open(d_r_path, 'w') as writer:\n",
+ " dog_breeds = reader.readlines()\n",
+ " writer.writelines(reversed(dog_breeds))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Working With Bytes [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Sometimes, you may need to work with files using byte strings. This is done by adding the `'b'` character to the mode argument. All of the same methods for the file object apply. However, each of the methods expect and return a bytes object instead:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "b'this is line 1\\n'\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('myfile.txt', 'rb') as reader:\n",
+ " print(reader.readline())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Opening a text file using the `b` flag isn’t that interesting. Let’s say we have this picture of python logo (`python.png`):"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can actually open that file in Python and examine the contents! Since the `.png` file format is well defined, the header of the file is 8 bytes broken up like this:"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "|Value\t|Interpretation|\n",
+ "|:--|:--|\n",
+ "|`0x89`\t|A “magic” number to indicate that this is the start of a PNG|\n",
+ "|`0x50 0x4E 0x47`\t|PNG in ASCII|\n",
+ "|`0x0D 0x0A`\t|A DOS style line ending `\\r\\n`|\n",
+ "|`0x1A`\t|A DOS style EOF character|\n",
+ "|`0x0A`\t|A Unix style line ending `\\n`|"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Sure enough, when you open the file and read these bytes individually, you can see that this is indeed a `.png` header file:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "b'\\x89'\n",
+ "b'PNG'\n",
+ "b'\\r\\n'\n",
+ "b'\\x1a'\n",
+ "b'\\n'\n"
+ ]
+ }
+ ],
+ "source": [
+ "with open('./images/python.png', 'rb') as byte_reader:\n",
+ " print(byte_reader.read(1))\n",
+ " print(byte_reader.read(3))\n",
+ " print(byte_reader.read(2))\n",
+ " print(byte_reader.read(1))\n",
+ " print(byte_reader.read(1))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Summary [
](#table_of_contents)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "|Character|Meaning|\n",
+ "|:--|:--|\n",
+ "|`r`|open for reading (default)|\n",
+ "|`w`|open for writing, truncating the file first|\n",
+ "|`x`|create a new file and open it for writing|\n",
+ "|`a`|open for writing, appending to the end of the file if it exists|\n",
+ "|`b`|binary mode|\n",
+ "|`t`|text mode (default)|\n",
+ "|`+`|open a disk file for updating (reading and writing)|\n",
+ "|`U`|universal newline mode (deprecated)|"
+ ]
+ }
+ ],
+ "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.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/python/02. Object Oriented Programming/images/difference.webp b/Python/02. Object Oriented Programming/images/difference.webp
similarity index 100%
rename from python/02. Object Oriented Programming/images/difference.webp
rename to Python/02. Object Oriented Programming/images/difference.webp
diff --git a/Python/02. Object Oriented Programming/images/fileformat.webp b/Python/02. Object Oriented Programming/images/fileformat.webp
new file mode 100644
index 0000000..f15eda3
Binary files /dev/null and b/Python/02. Object Oriented Programming/images/fileformat.webp differ
diff --git a/python/02. Object Oriented Programming/images/intersection.webp b/Python/02. Object Oriented Programming/images/intersection.webp
similarity index 100%
rename from python/02. Object Oriented Programming/images/intersection.webp
rename to Python/02. Object Oriented Programming/images/intersection.webp
diff --git a/python/02. Object Oriented Programming/images/multi-diff.webp b/Python/02. Object Oriented Programming/images/multi-diff.webp
similarity index 100%
rename from python/02. Object Oriented Programming/images/multi-diff.webp
rename to Python/02. Object Oriented Programming/images/multi-diff.webp
diff --git a/Python/02. Object Oriented Programming/images/python.png b/Python/02. Object Oriented Programming/images/python.png
new file mode 100644
index 0000000..d8ed750
Binary files /dev/null and b/Python/02. Object Oriented Programming/images/python.png differ
diff --git a/python/02. Object Oriented Programming/images/sym-diff.webp b/Python/02. Object Oriented Programming/images/sym-diff.webp
similarity index 100%
rename from python/02. Object Oriented Programming/images/sym-diff.webp
rename to Python/02. Object Oriented Programming/images/sym-diff.webp
diff --git a/python/02. Object Oriented Programming/images/union.png b/Python/02. Object Oriented Programming/images/union.png
similarity index 100%
rename from python/02. Object Oriented Programming/images/union.png
rename to Python/02. Object Oriented Programming/images/union.png
diff --git a/python/03. Modular Programming/07.1 Modular Programming (Part #1: Modules).ipynb b/Python/03. Modular Programming/07.1 Modular Programming (Part #1: Modules).ipynb
similarity index 56%
rename from python/03. Modular Programming/07.1 Modular Programming (Part #1: Modules).ipynb
rename to Python/03. Modular Programming/07.1 Modular Programming (Part #1: Modules).ipynb
index 7a6d591..1e2e188 100755
--- a/python/03. Modular Programming/07.1 Modular Programming (Part #1: Modules).ipynb
+++ b/Python/03. Modular Programming/07.1 Modular Programming (Part #1: Modules).ipynb
@@ -2,15 +2,42 @@
"cells": [
{
"cell_type": "markdown",
- "id": "alternate-operations",
"metadata": {},
"source": [
- "# Modular Programming"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Modular Programming \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Overview](#overview)\n",
+ "* [The Module Search Path](#the_module_search_path)\n",
+ "* [The `import` Statement](#the_`import`_statement)\n",
+ " * [`from import `](#`from__import_`)\n",
+ " * [`from import as `](#`from__import__as_`)\n",
+ " * [`import as `](#`import__as_`)\n",
+ "* [The `dir()` Function](#the_`dir()`_function)\n",
+ "* [Executing a Module as a Script](#executing_a_module_as_a_script)\n",
+ "* [Reloading a Module](#reloading_a_module)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "metropolitan-planning",
"metadata": {},
"source": [
"**Modular** programming refers to the process of breaking a large, unwieldy programming task into separate, smaller, more manageable subtasks or **modules**. Individual modules can then be cobbled together like building blocks to create a larger application."
@@ -18,7 +45,6 @@
},
{
"cell_type": "markdown",
- "id": "changed-importance",
"metadata": {},
"source": [
"There are several advantages to modularizing code in a large application:\n",
@@ -34,7 +60,6 @@
},
{
"cell_type": "markdown",
- "id": "yellow-incentive",
"metadata": {},
"source": [
"**Functions**, **modules** and **packages** are all constructs in Python that promote code modularization."
@@ -42,15 +67,15 @@
},
{
"cell_type": "markdown",
- "id": "iraqi-retail",
"metadata": {},
"source": [
- "## Overview"
+ "\n",
+ "## Overview [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "suspected-september",
"metadata": {},
"source": [
"There are actually three different ways to define a module in Python:\n",
@@ -62,7 +87,6 @@
},
{
"cell_type": "markdown",
- "id": "about-drive",
"metadata": {},
"source": [
"A module’s contents are accessed the same way in all three cases: with the `import` statement."
@@ -70,16 +94,15 @@
},
{
"cell_type": "markdown",
- "id": "parliamentary-berlin",
"metadata": {},
"source": [
- "## The Module Search Path"
+ "\n",
+ "## The Module Search Path [
](#table_of_contents)\n"
]
},
{
"cell_type": "code",
- "execution_count": 1,
- "id": "portable-malaysia",
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@@ -88,7 +111,6 @@
},
{
"cell_type": "markdown",
- "id": "employed-avenue",
"metadata": {},
"source": [
"When the interpreter executes the above import statement, it searches for mod.py in a list of directories assembled from the following sources:"
@@ -96,7 +118,6 @@
},
{
"cell_type": "markdown",
- "id": "jewish-buddy",
"metadata": {},
"source": [
"- The directory from which the input script was run or the current directory if the interpreter is being run interactively\n",
@@ -106,7 +127,6 @@
},
{
"cell_type": "markdown",
- "id": "according-occasion",
"metadata": {},
"source": [
"The resulting search path is accessible in the Python variable `sys.path`, which is obtained from a module named `sys`:"
@@ -115,20 +135,20 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "cardiac-sleep",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "['/mnt/c/Users/hejaz/OneDrive/Desktop/CS-Tutorial/Python/03. Modular Programming',\n",
- " '/home/ali/anaconda3/envs/test/lib/python37.zip',\n",
- " '/home/ali/anaconda3/envs/test/lib/python3.7',\n",
- " '/home/ali/anaconda3/envs/test/lib/python3.7/lib-dynload',\n",
+ "['/Users/ali/PERSONAL_DIR/github/CS-Tutorial/python/03. Modular Programming',\n",
+ " '/Users/ali/opt/anaconda3/envs/py37/lib/python37.zip',\n",
+ " '/Users/ali/opt/anaconda3/envs/py37/lib/python3.7',\n",
+ " '/Users/ali/opt/anaconda3/envs/py37/lib/python3.7/lib-dynload',\n",
" '',\n",
- " '/home/ali/anaconda3/envs/test/lib/python3.7/site-packages',\n",
- " '/home/ali/anaconda3/envs/test/lib/python3.7/site-packages/IPython/extensions',\n",
- " '/home/ali/.ipython']"
+ " '/Users/ali/.local/lib/python3.7/site-packages',\n",
+ " '/Users/ali/opt/anaconda3/envs/py37/lib/python3.7/site-packages',\n",
+ " '/Users/ali/opt/anaconda3/envs/py37/lib/python3.7/site-packages/IPython/extensions',\n",
+ " '/Users/ali/.ipython']"
]
},
"execution_count": 1,
@@ -143,7 +163,6 @@
},
{
"cell_type": "markdown",
- "id": "chief-impression",
"metadata": {},
"source": [
"- Put `mod.py` in the directory where the input script is located or the current directory, if interactive\n",
@@ -154,7 +173,6 @@
},
{
"cell_type": "markdown",
- "id": "increasing-stations",
"metadata": {},
"source": [
"There is actually one additional option: you can put the module file in any directory of your choice and then modify `sys.path` at run-time so that it contains that directory. For example, in this case, you could put `mod.py` in directory `C:\\Users\\john` and then issue the following statements:"
@@ -163,7 +181,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "educational-baker",
"metadata": {},
"outputs": [],
"source": [
@@ -173,7 +190,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "independent-oracle",
"metadata": {},
"outputs": [
{
@@ -201,7 +217,6 @@
},
{
"cell_type": "markdown",
- "id": "cardiac-allocation",
"metadata": {},
"source": [
"Once a module has been imported, you can determine the location where it was found with the module’s `__file__` attribute:"
@@ -209,18 +224,17 @@
},
{
"cell_type": "code",
- "execution_count": 9,
- "id": "moved-disability",
+ "execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "('/home/ali/anaconda3/envs/test/lib/python3.7/site-packages/numpy/__init__.py',\n",
- " '/home/ali/anaconda3/envs/test/lib/python3.7/site-packages/matplotlib/__init__.py')"
+ "('/Users/ali/opt/anaconda3/envs/py37/lib/python3.7/site-packages/numpy/__init__.py',\n",
+ " '/Users/ali/opt/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/__init__.py')"
]
},
- "execution_count": 9,
+ "execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
@@ -234,7 +248,6 @@
},
{
"cell_type": "markdown",
- "id": "severe-surgeon",
"metadata": {},
"source": [
"The directory portion of `__file__` should be one of the directories in `sys.path`."
@@ -242,15 +255,15 @@
},
{
"cell_type": "markdown",
- "id": "approximate-wiring",
"metadata": {},
"source": [
- "## The `import` Statement"
+ "\n",
+ "## The `import` Statement [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "eleven-claim",
"metadata": {},
"source": [
"**Module** contents are made available to the caller with the `import` statement. The `import` statement takes many different forms, shown below.\n",
@@ -262,7 +275,6 @@
},
{
"cell_type": "markdown",
- "id": "living-tuition",
"metadata": {},
"source": [
"Note that this does not make the module contents directly accessible to the caller. Each module has its own **private symbol table**, which serves as the global symbol table for all objects defined in the module. Thus, a module creates a separate **namespace**, as already noted.\n",
@@ -272,7 +284,6 @@
},
{
"cell_type": "markdown",
- "id": "pressing-drama",
"metadata": {},
"source": [
"From the caller, objects in the module are only accessible when prefixed with `` via dot notation, as illustrated below."
@@ -281,7 +292,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "handed-boundary",
"metadata": {},
"outputs": [
{
@@ -304,7 +314,6 @@
},
{
"cell_type": "markdown",
- "id": "isolated-rochester",
"metadata": {},
"source": [
"Several comma-separated modules may be specified in a single import statement:\n",
@@ -316,15 +325,14 @@
},
{
"cell_type": "markdown",
- "id": "cutting-thailand",
"metadata": {},
"source": [
- "### `from import `"
+ "_import_`\">\n",
+ "### `from import ` [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "alpha-commons",
"metadata": {},
"source": [
"An alternate form of the `import` statement allows individual objects from the module to be imported directly into the caller’s symbol table:\n",
@@ -336,7 +344,6 @@
},
{
"cell_type": "markdown",
- "id": "aware-macintosh",
"metadata": {},
"source": [
"Following execution of the above statement, `` can be referenced in the caller’s environment without the `` prefix:"
@@ -344,7 +351,6 @@
},
{
"cell_type": "markdown",
- "id": "eligible-wisconsin",
"metadata": {},
"source": [
"**Note:** Because this form of import places the object names directly into the caller’s symbol table, any objects that already exist with the same name will be overwritten."
@@ -353,7 +359,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "thorough-coating",
"metadata": {},
"outputs": [
{
@@ -375,7 +380,6 @@
},
{
"cell_type": "markdown",
- "id": "rotary-madagascar",
"metadata": {},
"source": [
"It is even possible to indiscriminately import everything from a module at one fell swoop:\n",
@@ -387,7 +391,6 @@
},
{
"cell_type": "markdown",
- "id": "loved-bobby",
"metadata": {},
"source": [
"This will place the names of all objects from into the local symbol table, with the exception of any that begin with the underscore `(_)` character."
@@ -395,8 +398,7 @@
},
{
"cell_type": "code",
- "execution_count": 9,
- "id": "crude-blood",
+ "execution_count": 19,
"metadata": {},
"outputs": [
{
@@ -405,7 +407,7 @@
""
]
},
- "execution_count": 9,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@@ -418,7 +420,6 @@
},
{
"cell_type": "markdown",
- "id": "medieval-chase",
"metadata": {},
"source": [
"**Note:** This isn’t necessarily recommended in large-scale production code. It’s a bit dangerous because you are entering names into the local symbol table"
@@ -426,7 +427,6 @@
},
{
"cell_type": "markdown",
- "id": "verbal-answer",
"metadata": {},
"source": [
"Unless you know them all well and can be confident there won’t be a conflict, you have a decent chance of overwriting an existing name inadvertently. However, this syntax is quite handy when you are just mucking around with the interactive interpreter, for testing or discovery purposes, because it quickly gives you access to everything a module has to offer without a lot of typing."
@@ -434,15 +434,15 @@
},
{
"cell_type": "markdown",
- "id": "angry-genesis",
"metadata": {},
"source": [
- "### `from import as `"
+ "_import__as_`\">\n",
+ "### `from import as ` [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "consecutive-functionality",
"metadata": {},
"source": [
"It is also possible to import individual objects but enter them into the local symbol table with alternate names:\n",
@@ -454,7 +454,6 @@
},
{
"cell_type": "markdown",
- "id": "alert-passenger",
"metadata": {},
"source": [
"This makes it possible to place names directly into the local symbol table but avoid conflicts with previously existing names:"
@@ -463,7 +462,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "tamil-drama",
"metadata": {},
"outputs": [
{
@@ -487,16 +485,16 @@
},
{
"cell_type": "markdown",
- "id": "reliable-apparatus",
"metadata": {},
"source": [
- "### `import as `"
+ "_as_`\">\n",
+ "### `import as ` [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
- "id": "returning-animal",
"metadata": {},
"outputs": [
{
@@ -518,7 +516,6 @@
},
{
"cell_type": "markdown",
- "id": "extreme-grove",
"metadata": {},
"source": [
"Module contents can be imported from within a function definition. In that case, the `import` does not occur until the function is called:"
@@ -527,7 +524,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "technical-prerequisite",
"metadata": {},
"outputs": [],
"source": [
@@ -539,7 +535,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "advance-forest",
"metadata": {},
"outputs": [
{
@@ -559,7 +554,6 @@
},
{
"cell_type": "markdown",
- "id": "mineral-thunder",
"metadata": {},
"source": [
"However, Python 3 does not allow the indiscriminate `import *` syntax from within a function:"
@@ -568,7 +562,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "engaging-evening",
"metadata": {},
"outputs": [
{
@@ -587,15 +580,15 @@
},
{
"cell_type": "markdown",
- "id": "adopted-adventure",
"metadata": {},
"source": [
- "## The `dir()` Function"
+ "\n",
+ "## The `dir()` Function [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "experienced-value",
"metadata": {},
"source": [
"The built-in function `dir()` returns a list of defined names in a namespace. Without arguments, it produces an alphabetically sorted list of names in the current **local symbol table**. This can be useful for identifying what exactly has been added to the namespace by an import statement:"
@@ -603,629 +596,9 @@
},
{
"cell_type": "code",
- "execution_count": 15,
- "id": "ranking-antibody",
+ "execution_count": 4,
"metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "['ALLOW_THREADS',\n",
- " 'AxisError',\n",
- " 'BUFSIZE',\n",
- " 'Bytes0',\n",
- " 'CLIP',\n",
- " 'ComplexWarning',\n",
- " 'DataSource',\n",
- " 'Datetime64',\n",
- " 'ERR_CALL',\n",
- " 'ERR_DEFAULT',\n",
- " 'ERR_IGNORE',\n",
- " 'ERR_LOG',\n",
- " 'ERR_PRINT',\n",
- " 'ERR_RAISE',\n",
- " 'ERR_WARN',\n",
- " 'FLOATING_POINT_SUPPORT',\n",
- " 'FPE_DIVIDEBYZERO',\n",
- " 'FPE_INVALID',\n",
- " 'FPE_OVERFLOW',\n",
- " 'FPE_UNDERFLOW',\n",
- " 'False_',\n",
- " 'Inf',\n",
- " 'Infinity',\n",
- " 'MAXDIMS',\n",
- " 'MAY_SHARE_BOUNDS',\n",
- " 'MAY_SHARE_EXACT',\n",
- " 'MachAr',\n",
- " 'ModuleDeprecationWarning',\n",
- " 'NAN',\n",
- " 'NINF',\n",
- " 'NZERO',\n",
- " 'NaN',\n",
- " 'PINF',\n",
- " 'PZERO',\n",
- " 'RAISE',\n",
- " 'RankWarning',\n",
- " 'SHIFT_DIVIDEBYZERO',\n",
- " 'SHIFT_INVALID',\n",
- " 'SHIFT_OVERFLOW',\n",
- " 'SHIFT_UNDERFLOW',\n",
- " 'ScalarType',\n",
- " 'Str0',\n",
- " 'Tester',\n",
- " 'TooHardError',\n",
- " 'True_',\n",
- " 'UFUNC_BUFSIZE_DEFAULT',\n",
- " 'UFUNC_PYVALS_NAME',\n",
- " 'Uint64',\n",
- " 'VisibleDeprecationWarning',\n",
- " 'WRAP',\n",
- " '_NoValue',\n",
- " '_UFUNC_API',\n",
- " '__NUMPY_SETUP__',\n",
- " '__all__',\n",
- " '__builtins__',\n",
- " '__cached__',\n",
- " '__config__',\n",
- " '__deprecated_attrs__',\n",
- " '__dir__',\n",
- " '__doc__',\n",
- " '__expired_functions__',\n",
- " '__file__',\n",
- " '__getattr__',\n",
- " '__git_revision__',\n",
- " '__loader__',\n",
- " '__name__',\n",
- " '__package__',\n",
- " '__path__',\n",
- " '__spec__',\n",
- " '__version__',\n",
- " '_add_newdoc_ufunc',\n",
- " '_distributor_init',\n",
- " '_financial_names',\n",
- " '_globals',\n",
- " '_mat',\n",
- " '_pytesttester',\n",
- " 'abs',\n",
- " 'absolute',\n",
- " 'add',\n",
- " 'add_docstring',\n",
- " 'add_newdoc',\n",
- " 'add_newdoc_ufunc',\n",
- " 'alen',\n",
- " 'all',\n",
- " 'allclose',\n",
- " 'alltrue',\n",
- " 'amax',\n",
- " 'amin',\n",
- " 'angle',\n",
- " 'any',\n",
- " 'append',\n",
- " 'apply_along_axis',\n",
- " 'apply_over_axes',\n",
- " 'arange',\n",
- " 'arccos',\n",
- " 'arccosh',\n",
- " 'arcsin',\n",
- " 'arcsinh',\n",
- " 'arctan',\n",
- " 'arctan2',\n",
- " 'arctanh',\n",
- " 'argmax',\n",
- " 'argmin',\n",
- " 'argpartition',\n",
- " 'argsort',\n",
- " 'argwhere',\n",
- " 'around',\n",
- " 'array',\n",
- " 'array2string',\n",
- " 'array_equal',\n",
- " 'array_equiv',\n",
- " 'array_repr',\n",
- " 'array_split',\n",
- " 'array_str',\n",
- " 'asanyarray',\n",
- " 'asarray',\n",
- " 'asarray_chkfinite',\n",
- " 'ascontiguousarray',\n",
- " 'asfarray',\n",
- " 'asfortranarray',\n",
- " 'asmatrix',\n",
- " 'asscalar',\n",
- " 'atleast_1d',\n",
- " 'atleast_2d',\n",
- " 'atleast_3d',\n",
- " 'average',\n",
- " 'bartlett',\n",
- " 'base_repr',\n",
- " 'binary_repr',\n",
- " 'bincount',\n",
- " 'bitwise_and',\n",
- " 'bitwise_not',\n",
- " 'bitwise_or',\n",
- " 'bitwise_xor',\n",
- " 'blackman',\n",
- " 'block',\n",
- " 'bmat',\n",
- " 'bool8',\n",
- " 'bool_',\n",
- " 'broadcast',\n",
- " 'broadcast_arrays',\n",
- " 'broadcast_shapes',\n",
- " 'broadcast_to',\n",
- " 'busday_count',\n",
- " 'busday_offset',\n",
- " 'busdaycalendar',\n",
- " 'byte',\n",
- " 'byte_bounds',\n",
- " 'bytes0',\n",
- " 'bytes_',\n",
- " 'c_',\n",
- " 'can_cast',\n",
- " 'cast',\n",
- " 'cbrt',\n",
- " 'cdouble',\n",
- " 'ceil',\n",
- " 'cfloat',\n",
- " 'char',\n",
- " 'character',\n",
- " 'chararray',\n",
- " 'choose',\n",
- " 'clip',\n",
- " 'clongdouble',\n",
- " 'clongfloat',\n",
- " 'column_stack',\n",
- " 'common_type',\n",
- " 'compare_chararrays',\n",
- " 'compat',\n",
- " 'complex128',\n",
- " 'complex256',\n",
- " 'complex64',\n",
- " 'complex_',\n",
- " 'complexfloating',\n",
- " 'compress',\n",
- " 'concatenate',\n",
- " 'conj',\n",
- " 'conjugate',\n",
- " 'convolve',\n",
- " 'copy',\n",
- " 'copysign',\n",
- " 'copyto',\n",
- " 'core',\n",
- " 'corrcoef',\n",
- " 'correlate',\n",
- " 'cos',\n",
- " 'cosh',\n",
- " 'count_nonzero',\n",
- " 'cov',\n",
- " 'cross',\n",
- " 'csingle',\n",
- " 'ctypeslib',\n",
- " 'cumprod',\n",
- " 'cumproduct',\n",
- " 'cumsum',\n",
- " 'datetime64',\n",
- " 'datetime_as_string',\n",
- " 'datetime_data',\n",
- " 'deg2rad',\n",
- " 'degrees',\n",
- " 'delete',\n",
- " 'deprecate',\n",
- " 'deprecate_with_doc',\n",
- " 'diag',\n",
- " 'diag_indices',\n",
- " 'diag_indices_from',\n",
- " 'diagflat',\n",
- " 'diagonal',\n",
- " 'diff',\n",
- " 'digitize',\n",
- " 'disp',\n",
- " 'divide',\n",
- " 'divmod',\n",
- " 'dot',\n",
- " 'double',\n",
- " 'dsplit',\n",
- " 'dstack',\n",
- " 'dtype',\n",
- " 'e',\n",
- " 'ediff1d',\n",
- " 'einsum',\n",
- " 'einsum_path',\n",
- " 'emath',\n",
- " 'empty',\n",
- " 'empty_like',\n",
- " 'equal',\n",
- " 'errstate',\n",
- " 'euler_gamma',\n",
- " 'exp',\n",
- " 'exp2',\n",
- " 'expand_dims',\n",
- " 'expm1',\n",
- " 'extract',\n",
- " 'eye',\n",
- " 'fabs',\n",
- " 'fastCopyAndTranspose',\n",
- " 'fft',\n",
- " 'fill_diagonal',\n",
- " 'find_common_type',\n",
- " 'finfo',\n",
- " 'fix',\n",
- " 'flatiter',\n",
- " 'flatnonzero',\n",
- " 'flexible',\n",
- " 'flip',\n",
- " 'fliplr',\n",
- " 'flipud',\n",
- " 'float128',\n",
- " 'float16',\n",
- " 'float32',\n",
- " 'float64',\n",
- " 'float_',\n",
- " 'float_power',\n",
- " 'floating',\n",
- " 'floor',\n",
- " 'floor_divide',\n",
- " 'fmax',\n",
- " 'fmin',\n",
- " 'fmod',\n",
- " 'format_float_positional',\n",
- " 'format_float_scientific',\n",
- " 'format_parser',\n",
- " 'frexp',\n",
- " 'frombuffer',\n",
- " 'fromfile',\n",
- " 'fromfunction',\n",
- " 'fromiter',\n",
- " 'frompyfunc',\n",
- " 'fromregex',\n",
- " 'fromstring',\n",
- " 'full',\n",
- " 'full_like',\n",
- " 'gcd',\n",
- " 'generic',\n",
- " 'genfromtxt',\n",
- " 'geomspace',\n",
- " 'get_array_wrap',\n",
- " 'get_include',\n",
- " 'get_printoptions',\n",
- " 'getbufsize',\n",
- " 'geterr',\n",
- " 'geterrcall',\n",
- " 'geterrobj',\n",
- " 'gradient',\n",
- " 'greater',\n",
- " 'greater_equal',\n",
- " 'half',\n",
- " 'hamming',\n",
- " 'hanning',\n",
- " 'heaviside',\n",
- " 'histogram',\n",
- " 'histogram2d',\n",
- " 'histogram_bin_edges',\n",
- " 'histogramdd',\n",
- " 'hsplit',\n",
- " 'hstack',\n",
- " 'hypot',\n",
- " 'i0',\n",
- " 'identity',\n",
- " 'iinfo',\n",
- " 'imag',\n",
- " 'in1d',\n",
- " 'index_exp',\n",
- " 'indices',\n",
- " 'inexact',\n",
- " 'inf',\n",
- " 'info',\n",
- " 'infty',\n",
- " 'inner',\n",
- " 'insert',\n",
- " 'int0',\n",
- " 'int16',\n",
- " 'int32',\n",
- " 'int64',\n",
- " 'int8',\n",
- " 'int_',\n",
- " 'intc',\n",
- " 'integer',\n",
- " 'interp',\n",
- " 'intersect1d',\n",
- " 'intp',\n",
- " 'invert',\n",
- " 'is_busday',\n",
- " 'isclose',\n",
- " 'iscomplex',\n",
- " 'iscomplexobj',\n",
- " 'isfinite',\n",
- " 'isfortran',\n",
- " 'isin',\n",
- " 'isinf',\n",
- " 'isnan',\n",
- " 'isnat',\n",
- " 'isneginf',\n",
- " 'isposinf',\n",
- " 'isreal',\n",
- " 'isrealobj',\n",
- " 'isscalar',\n",
- " 'issctype',\n",
- " 'issubclass_',\n",
- " 'issubdtype',\n",
- " 'issubsctype',\n",
- " 'iterable',\n",
- " 'ix_',\n",
- " 'kaiser',\n",
- " 'kernel_version',\n",
- " 'kron',\n",
- " 'lcm',\n",
- " 'ldexp',\n",
- " 'left_shift',\n",
- " 'less',\n",
- " 'less_equal',\n",
- " 'lexsort',\n",
- " 'lib',\n",
- " 'linalg',\n",
- " 'linspace',\n",
- " 'little_endian',\n",
- " 'load',\n",
- " 'loads',\n",
- " 'loadtxt',\n",
- " 'log',\n",
- " 'log10',\n",
- " 'log1p',\n",
- " 'log2',\n",
- " 'logaddexp',\n",
- " 'logaddexp2',\n",
- " 'logical_and',\n",
- " 'logical_not',\n",
- " 'logical_or',\n",
- " 'logical_xor',\n",
- " 'logspace',\n",
- " 'longcomplex',\n",
- " 'longdouble',\n",
- " 'longfloat',\n",
- " 'longlong',\n",
- " 'lookfor',\n",
- " 'ma',\n",
- " 'mafromtxt',\n",
- " 'mask_indices',\n",
- " 'mat',\n",
- " 'math',\n",
- " 'matmul',\n",
- " 'matrix',\n",
- " 'matrixlib',\n",
- " 'max',\n",
- " 'maximum',\n",
- " 'maximum_sctype',\n",
- " 'may_share_memory',\n",
- " 'mean',\n",
- " 'median',\n",
- " 'memmap',\n",
- " 'meshgrid',\n",
- " 'mgrid',\n",
- " 'min',\n",
- " 'min_scalar_type',\n",
- " 'minimum',\n",
- " 'mintypecode',\n",
- " 'mod',\n",
- " 'modf',\n",
- " 'moveaxis',\n",
- " 'msort',\n",
- " 'multiply',\n",
- " 'nan',\n",
- " 'nan_to_num',\n",
- " 'nanargmax',\n",
- " 'nanargmin',\n",
- " 'nancumprod',\n",
- " 'nancumsum',\n",
- " 'nanmax',\n",
- " 'nanmean',\n",
- " 'nanmedian',\n",
- " 'nanmin',\n",
- " 'nanpercentile',\n",
- " 'nanprod',\n",
- " 'nanquantile',\n",
- " 'nanstd',\n",
- " 'nansum',\n",
- " 'nanvar',\n",
- " 'nbytes',\n",
- " 'ndarray',\n",
- " 'ndenumerate',\n",
- " 'ndfromtxt',\n",
- " 'ndim',\n",
- " 'ndindex',\n",
- " 'nditer',\n",
- " 'negative',\n",
- " 'nested_iters',\n",
- " 'newaxis',\n",
- " 'nextafter',\n",
- " 'nonzero',\n",
- " 'not_equal',\n",
- " 'numarray',\n",
- " 'number',\n",
- " 'obj2sctype',\n",
- " 'object0',\n",
- " 'object_',\n",
- " 'ogrid',\n",
- " 'oldnumeric',\n",
- " 'ones',\n",
- " 'ones_like',\n",
- " 'os',\n",
- " 'outer',\n",
- " 'packbits',\n",
- " 'pad',\n",
- " 'partition',\n",
- " 'percentile',\n",
- " 'pi',\n",
- " 'piecewise',\n",
- " 'place',\n",
- " 'poly',\n",
- " 'poly1d',\n",
- " 'polyadd',\n",
- " 'polyder',\n",
- " 'polydiv',\n",
- " 'polyfit',\n",
- " 'polyint',\n",
- " 'polymul',\n",
- " 'polynomial',\n",
- " 'polysub',\n",
- " 'polyval',\n",
- " 'positive',\n",
- " 'power',\n",
- " 'printoptions',\n",
- " 'prod',\n",
- " 'product',\n",
- " 'promote_types',\n",
- " 'ptp',\n",
- " 'put',\n",
- " 'put_along_axis',\n",
- " 'putmask',\n",
- " 'quantile',\n",
- " 'r_',\n",
- " 'rad2deg',\n",
- " 'radians',\n",
- " 'random',\n",
- " 'ravel',\n",
- " 'ravel_multi_index',\n",
- " 'real',\n",
- " 'real_if_close',\n",
- " 'rec',\n",
- " 'recarray',\n",
- " 'recfromcsv',\n",
- " 'recfromtxt',\n",
- " 'reciprocal',\n",
- " 'record',\n",
- " 'remainder',\n",
- " 'repeat',\n",
- " 'require',\n",
- " 'reshape',\n",
- " 'resize',\n",
- " 'result_type',\n",
- " 'right_shift',\n",
- " 'rint',\n",
- " 'roll',\n",
- " 'rollaxis',\n",
- " 'roots',\n",
- " 'rot90',\n",
- " 'round',\n",
- " 'round_',\n",
- " 'row_stack',\n",
- " 's_',\n",
- " 'safe_eval',\n",
- " 'save',\n",
- " 'savetxt',\n",
- " 'savez',\n",
- " 'savez_compressed',\n",
- " 'sctype2char',\n",
- " 'sctypeDict',\n",
- " 'sctypes',\n",
- " 'searchsorted',\n",
- " 'select',\n",
- " 'set_numeric_ops',\n",
- " 'set_printoptions',\n",
- " 'set_string_function',\n",
- " 'setbufsize',\n",
- " 'setdiff1d',\n",
- " 'seterr',\n",
- " 'seterrcall',\n",
- " 'seterrobj',\n",
- " 'setxor1d',\n",
- " 'shape',\n",
- " 'shares_memory',\n",
- " 'short',\n",
- " 'show_config',\n",
- " 'sign',\n",
- " 'signbit',\n",
- " 'signedinteger',\n",
- " 'sin',\n",
- " 'sinc',\n",
- " 'single',\n",
- " 'singlecomplex',\n",
- " 'sinh',\n",
- " 'size',\n",
- " 'sometrue',\n",
- " 'sort',\n",
- " 'sort_complex',\n",
- " 'source',\n",
- " 'spacing',\n",
- " 'split',\n",
- " 'sqrt',\n",
- " 'square',\n",
- " 'squeeze',\n",
- " 'stack',\n",
- " 'std',\n",
- " 'str0',\n",
- " 'str_',\n",
- " 'string_',\n",
- " 'subtract',\n",
- " 'sum',\n",
- " 'swapaxes',\n",
- " 'sys',\n",
- " 'take',\n",
- " 'take_along_axis',\n",
- " 'tan',\n",
- " 'tanh',\n",
- " 'tensordot',\n",
- " 'test',\n",
- " 'testing',\n",
- " 'tile',\n",
- " 'timedelta64',\n",
- " 'trace',\n",
- " 'tracemalloc_domain',\n",
- " 'transpose',\n",
- " 'trapz',\n",
- " 'tri',\n",
- " 'tril',\n",
- " 'tril_indices',\n",
- " 'tril_indices_from',\n",
- " 'trim_zeros',\n",
- " 'triu',\n",
- " 'triu_indices',\n",
- " 'triu_indices_from',\n",
- " 'true_divide',\n",
- " 'trunc',\n",
- " 'typeDict',\n",
- " 'typecodes',\n",
- " 'typename',\n",
- " 'ubyte',\n",
- " 'ufunc',\n",
- " 'uint',\n",
- " 'uint0',\n",
- " 'uint16',\n",
- " 'uint32',\n",
- " 'uint64',\n",
- " 'uint8',\n",
- " 'uintc',\n",
- " 'uintp',\n",
- " 'ulonglong',\n",
- " 'unicode_',\n",
- " 'union1d',\n",
- " 'unique',\n",
- " 'unpackbits',\n",
- " 'unravel_index',\n",
- " 'unsignedinteger',\n",
- " 'unwrap',\n",
- " 'use_hugepage',\n",
- " 'ushort',\n",
- " 'vander',\n",
- " 'var',\n",
- " 'vdot',\n",
- " 'vectorize',\n",
- " 'version',\n",
- " 'void',\n",
- " 'void0',\n",
- " 'vsplit',\n",
- " 'vstack',\n",
- " 'warnings',\n",
- " 'where',\n",
- " 'who',\n",
- " 'zeros',\n",
- " 'zeros_like']"
- ]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
+ "outputs": [],
"source": [
"import numpy\n",
"dir(numpy)"
@@ -1233,7 +606,6 @@
},
{
"cell_type": "markdown",
- "id": "lonely-archives",
"metadata": {},
"source": [
"Without arguments, `dir()` lists the names you have defined currently:"
@@ -1242,7 +614,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "absent-support",
"metadata": {},
"outputs": [
{
@@ -1283,7 +654,6 @@
},
{
"cell_type": "markdown",
- "id": "forced-treasurer",
"metadata": {},
"source": [
"Note that it lists all types of names: variables, modules, functions, etc."
@@ -1291,7 +661,6 @@
},
{
"cell_type": "markdown",
- "id": "earlier-currency",
"metadata": {},
"source": [
"`dir()` does not list the names of built-in functions and variables. If you want a list of those, they are defined in the standard module builtins:"
@@ -1300,7 +669,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "otherwise-glossary",
"metadata": {},
"outputs": [],
"source": [
@@ -1310,7 +678,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "understood-reggae",
"metadata": {},
"outputs": [
{
@@ -1482,15 +849,15 @@
},
{
"cell_type": "markdown",
- "id": "preceding-mystery",
"metadata": {},
"source": [
- "## Executing a Module as a Script"
+ "\n",
+ "## Executing a Module as a Script [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "intended-agriculture",
"metadata": {},
"source": [
"Any .py file that contains a **module** is essentially also a Python **script**, and there isn’t any reason it can’t be executed like one.\n",
@@ -1502,7 +869,6 @@
},
{
"cell_type": "markdown",
- "id": "through-southwest",
"metadata": {},
"source": [
"**Note:** Unfortunately, a module also generates output when imported as a module:"
@@ -1510,7 +876,6 @@
},
{
"cell_type": "markdown",
- "id": "loving-occupation",
"metadata": {},
"source": [
"This is probably not what you want. It isn’t usual for a module to generate output when it is imported. Wouldn’t it be nice if you could distinguish between when the file is loaded as a module and when it is run as a standalone script?"
@@ -1518,7 +883,6 @@
},
{
"cell_type": "markdown",
- "id": "starting-budapest",
"metadata": {},
"source": [
"When a `.py` file is imported as a module, Python sets the special dunder variable `__name__` to the name of the module. However, if a file is run as a standalone script, `__name__` is (creatively) set to the string `'__main__'`. Using this fact, you can discern which is the case at run-time and alter behavior accordingly:"
@@ -1527,7 +891,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "historic-poker",
"metadata": {},
"outputs": [
{
@@ -1548,7 +911,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "timely-engineer",
"metadata": {},
"outputs": [
{
@@ -1567,7 +929,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "incomplete-commission",
"metadata": {},
"outputs": [
{
@@ -1589,15 +950,15 @@
},
{
"cell_type": "markdown",
- "id": "timely-croatia",
"metadata": {},
"source": [
- "## Reloading a Module"
+ "\n",
+ "## Reloading a Module [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "returning-light",
"metadata": {},
"source": [
"For reasons of efficiency, a module is only loaded once per interpreter session. That is fine for function and class definitions, which typically make up the bulk of a module’s contents. But a module can contain executable statements as well, usually for initialization. Be aware that these statements will only be executed the first time a module is imported."
@@ -1620,7 +981,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/03. Modular Programming/07.2 Modular Programming (Part #2: Packages).ipynb b/Python/03. Modular Programming/07.2 Modular Programming (Part #2: Packages).ipynb
similarity index 89%
rename from python/03. Modular Programming/07.2 Modular Programming (Part #2: Packages).ipynb
rename to Python/03. Modular Programming/07.2 Modular Programming (Part #2: Packages).ipynb
index 8a67176..0c7fd41 100755
--- a/python/03. Modular Programming/07.2 Modular Programming (Part #2: Packages).ipynb
+++ b/Python/03. Modular Programming/07.2 Modular Programming (Part #2: Packages).ipynb
@@ -2,37 +2,46 @@
"cells": [
{
"cell_type": "markdown",
- "id": "involved-factor",
"metadata": {},
"source": [
- "# 07.2 Modular Programming (Part #2: Packages)"
+ "
"
]
},
{
"cell_type": "markdown",
- "id": "metropolitan-teacher",
"metadata": {},
"source": [
- "TOC:\n",
- "* [1. Packages](#packages)\n",
- "* [2. Package Initialization](#package_init)\n",
- "* [3. Importing `*` from a Package](#package_import_*)\n",
- "* [4. Subpackages](#subpackages)\n",
- "* [4. Summary](#summary)"
+ "#
07.2 Modular Programming (Part 2: Packages) \n"
]
},
{
"cell_type": "markdown",
- "id": "capital-extent",
"metadata": {},
"source": [
- "\n",
- "## 1. Packages"
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [1. Packages](#1._packages)\n",
+ "* [2. Package Initialization](#2._package_initialization)\n",
+ "* [3. Importing `*` From a Package](#3._importing_`*`_from_a_package)\n",
+ "* [4. Subpackages](#4._subpackages)\n",
+ "* [Summary](#summary)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## 1. Packages [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "front-india",
"metadata": {},
"source": [
"Suppose you have developed a very large application that includes many modules. As the number of modules grows, it becomes difficult to keep track of them all if they are dumped into one location. This is particularly so if they have similar names or functionality. You might wish for a means of grouping and organizing them."
@@ -40,7 +49,6 @@
},
{
"cell_type": "markdown",
- "id": "guided-metro",
"metadata": {},
"source": [
"**Packages** allow for a hierarchical structuring of the module namespace using **dot notation**.\n",
@@ -51,7 +59,6 @@
},
{
"cell_type": "markdown",
- "id": "imposed-statistics",
"metadata": {},
"source": [
"Creating a package is quite straightforward, since it makes use of the operating system’s inherent hierarchical file structure. Consider the following arrangement:\n",
@@ -61,7 +68,6 @@
},
{
"cell_type": "markdown",
- "id": "aboriginal-amendment",
"metadata": {},
"source": [
"Here, there is a directory named `pkg` that contains two modules, `mod1.py` and `mod2.py`. The contents of the modules are:"
@@ -70,7 +76,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "crude-syracuse",
"metadata": {},
"outputs": [
{
@@ -93,7 +98,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "alternate-amber",
"metadata": {},
"outputs": [
{
@@ -115,7 +119,6 @@
},
{
"cell_type": "markdown",
- "id": "honest-riverside",
"metadata": {},
"source": [
"Given this structure, if the `pkg` directory resides in a location where it can be found (in one of the directories contained in sys.path), you can refer to the two modules with **dot notation** (`pkg.mod1`, `pkg.mod2`) and import them with the syntax you are already familiar with:"
@@ -124,7 +127,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "systematic-insight",
"metadata": {},
"outputs": [
{
@@ -153,7 +155,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "chemical-travel",
"metadata": {},
"outputs": [],
"source": [
@@ -163,7 +164,6 @@
},
{
"cell_type": "markdown",
- "id": "oriented-shark",
"metadata": {},
"source": [
"You can technically import the package:"
@@ -172,7 +172,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "continued-works",
"metadata": {},
"outputs": [],
"source": [
@@ -182,7 +181,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "charitable-nutrition",
"metadata": {},
"outputs": [
{
@@ -202,7 +200,6 @@
},
{
"cell_type": "markdown",
- "id": "generic-sharp",
"metadata": {},
"source": [
"Though this is, strictly speaking, a syntactically correct Python statement, it doesn’t do much of anything useful. In particular, it does not place any of the modules in `pkg` into the local namespace:"
@@ -211,7 +208,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "invisible-palace",
"metadata": {},
"outputs": [
{
@@ -233,7 +229,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "orange-registrar",
"metadata": {},
"outputs": [
{
@@ -254,7 +249,6 @@
},
{
"cell_type": "markdown",
- "id": "internal-mixture",
"metadata": {},
"source": [
"To actually import the modules or their contents, you need to use one of the forms shown below:"
@@ -263,7 +257,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "judicial-dakota",
"metadata": {},
"outputs": [
{
@@ -287,7 +280,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "foster-terrain",
"metadata": {},
"outputs": [
{
@@ -310,7 +302,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "standing-military",
"metadata": {},
"outputs": [
{
@@ -332,17 +323,16 @@
},
{
"cell_type": "markdown",
- "id": "bronze-conflict",
"metadata": {},
"source": [
- "\n",
- "## 2. Package Initialization"
+ "\n",
+ "## 2. Package Initialization [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
- "id": "fabulous-central",
"metadata": {},
"outputs": [],
"source": [
@@ -352,7 +342,6 @@
},
{
"cell_type": "markdown",
- "id": "rising-greece",
"metadata": {},
"source": [
"If a file named `__init__.py` is present in a package directory, it is invoked when the package or a module in the package is imported. This can be used for execution of package initialization code, such as initialization of package-level data."
@@ -360,7 +349,6 @@
},
{
"cell_type": "markdown",
- "id": "color-compatibility",
"metadata": {},
"source": [
"For example, consider the following `__init__.py` file:"
@@ -369,7 +357,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "furnished-person",
"metadata": {},
"outputs": [
{
@@ -388,7 +375,6 @@
},
{
"cell_type": "markdown",
- "id": "confirmed-break",
"metadata": {},
"source": [
"Let’s add this file to the `pkg` directory from the above example:\n",
@@ -399,7 +385,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "closing-cutting",
"metadata": {},
"outputs": [],
"source": [
@@ -409,7 +394,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "advanced-liquid",
"metadata": {},
"outputs": [
{
@@ -439,7 +423,6 @@
},
{
"cell_type": "markdown",
- "id": "pending-compilation",
"metadata": {},
"source": [
"Now when the package is imported, the global list `A` is initialized:"
@@ -448,7 +431,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "assigned-lodge",
"metadata": {},
"outputs": [
{
@@ -468,7 +450,6 @@
},
{
"cell_type": "markdown",
- "id": "express-length",
"metadata": {},
"source": [
"A module in the package can access the global variable by importing it in turn:"
@@ -477,7 +458,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "deadly-laundry",
"metadata": {},
"outputs": [
{
@@ -503,7 +483,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "sitting-collective",
"metadata": {},
"outputs": [],
"source": [
@@ -513,7 +492,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "formal-alabama",
"metadata": {},
"outputs": [
{
@@ -542,7 +520,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "coordinated-white",
"metadata": {},
"outputs": [
{
@@ -562,7 +539,6 @@
},
{
"cell_type": "markdown",
- "id": "synthetic-disclaimer",
"metadata": {},
"source": [
"`__init__.py` can also be used to effect automatic importing of modules from a package. For example, earlier you saw that the statement `import pkg` only places the name `pkg` in the caller’s local symbol table and doesn’t import any modules. But if `__init__.py` in the `pkg` directory contains the following:"
@@ -571,7 +547,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "random-intention",
"metadata": {},
"outputs": [
{
@@ -591,7 +566,6 @@
},
{
"cell_type": "markdown",
- "id": "cordless-fisher",
"metadata": {},
"source": [
"Then when you execute `import pkg`, modules `mod1` and `mod2` are imported automatically."
@@ -600,7 +574,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "isolated-leather",
"metadata": {},
"outputs": [],
"source": [
@@ -610,7 +583,6 @@
{
"cell_type": "code",
"execution_count": 23,
- "id": "distant-blind",
"metadata": {},
"outputs": [
{
@@ -631,7 +603,6 @@
},
{
"cell_type": "markdown",
- "id": "crude-mailing",
"metadata": {},
"source": [
"**Note:** Much of the Python documentation states that an `__init__.py` file must be present in the package directory when creating a package. This was once true. It used to be that the very presence of `__init__.py` signified to Python that a package was being defined. The file could contain initialization code or even be empty, but **it had to be present**.\n",
@@ -641,16 +612,15 @@
},
{
"cell_type": "markdown",
- "id": "capable-drove",
"metadata": {},
"source": [
- "\n",
- "## 3. Importing `*` From a Package"
+ "\n",
+ "## 3. Importing `*` From a Package [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "delayed-settle",
"metadata": {},
"source": [
"You have already seen that when import * is used for a module, all objects from the module are imported into the local symbol table, except those whose names begin with an underscore, as always"
@@ -658,7 +628,6 @@
},
{
"cell_type": "markdown",
- "id": "mounted-daily",
"metadata": {},
"source": [
"The analogous statement for a **package** is this:\n",
@@ -671,7 +640,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "difficult-description",
"metadata": {},
"outputs": [],
"source": [
@@ -681,7 +649,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "global-segment",
"metadata": {},
"outputs": [
{
@@ -701,7 +668,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "guilty-coverage",
"metadata": {},
"outputs": [
{
@@ -719,7 +685,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "julian-passport",
"metadata": {},
"outputs": [
{
@@ -766,7 +731,6 @@
},
{
"cell_type": "markdown",
- "id": "gorgeous-basic",
"metadata": {},
"source": [
"You might have expected (assuming you had any expectations at all) that Python would dive down into the package directory, find all the modules it could, and import them all. But as you can see, by default that is not what happens."
@@ -774,7 +738,6 @@
},
{
"cell_type": "markdown",
- "id": "enclosed-escape",
"metadata": {},
"source": [
"Instead, Python follows this convention: if the `__init__.py` file in the package directory contains a list named `__all__`, it is taken to be a list of modules that should be imported when the statement from `` import `*` is encountered."
@@ -783,7 +746,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "strategic-entrepreneur",
"metadata": {},
"outputs": [
{
@@ -805,7 +767,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "abstract-container",
"metadata": {},
"outputs": [
{
@@ -824,7 +785,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "atomic-tokyo",
"metadata": {},
"outputs": [
{
@@ -873,7 +833,6 @@
},
{
"cell_type": "markdown",
- "id": "actual-enhancement",
"metadata": {},
"source": [
"Using `import *` still isn’t considered terrific form, any more for packages than for modules. But this facility at least gives the creator of the package some control over what happens when `import *` is specified. (In fact, it provides the capability to disallow it entirely, simply by declining to define `__all__` at all. As you have seen, the default behavior for packages is to import nothing.)"
@@ -881,7 +840,6 @@
},
{
"cell_type": "markdown",
- "id": "skilled-advance",
"metadata": {},
"source": [
"By the way, `__all__` can be defined in a module as well and serves the same purpose: to control what is imported with import `*`. For example, modify `mod1.py` as follows:"
@@ -890,7 +848,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "monthly-strength",
"metadata": {},
"outputs": [
{
@@ -914,7 +871,6 @@
},
{
"cell_type": "markdown",
- "id": "sensitive-royal",
"metadata": {},
"source": [
"Now `from pkg.mod1 import *` statement from pkg.mod1 will only import what is contained in `__all__`."
@@ -922,7 +878,6 @@
},
{
"cell_type": "markdown",
- "id": "selective-buffalo",
"metadata": {},
"source": [
"**Summary:**\n",
@@ -935,16 +890,15 @@
},
{
"cell_type": "markdown",
- "id": "exotic-yugoslavia",
"metadata": {},
"source": [
- "\n",
- "## 4. Subpackages"
+ "\n",
+ "## 4. Subpackages [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "adjusted-affiliation",
"metadata": {},
"source": [
"Packages can contain nested subpackages to arbitrary depth. For example, let’s make one more modification to the example package directory as follows:\n",
@@ -954,7 +908,6 @@
},
{
"cell_type": "markdown",
- "id": "exempt-breach",
"metadata": {},
"source": [
"Importing still works the same as shown previously. Syntax is similar, but additional **dot notation** is used to separate **package** name from **subpackage** name:"
@@ -963,7 +916,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "faced-coverage",
"metadata": {},
"outputs": [],
"source": [
@@ -973,7 +925,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "collect-husband",
"metadata": {},
"outputs": [
{
@@ -996,7 +947,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "gothic-beaver",
"metadata": {},
"outputs": [],
"source": [
@@ -1005,7 +955,6 @@
},
{
"cell_type": "markdown",
- "id": "aquatic-trade",
"metadata": {},
"source": [
"In addition, a module in one subpackage can reference objects in a **sibling subpackage** (in the event that the sibling contains some functionality that you need). For example, suppose you want to import and execute function foo() (defined in module mod1) from within module mod3. You can either use an **absolute import**:"
@@ -1014,7 +963,6 @@
{
"cell_type": "code",
"execution_count": 32,
- "id": "aboriginal-melbourne",
"metadata": {},
"outputs": [
{
@@ -1040,7 +988,6 @@
{
"cell_type": "code",
"execution_count": 35,
- "id": "available-chester",
"metadata": {},
"outputs": [
{
@@ -1057,7 +1004,6 @@
},
{
"cell_type": "markdown",
- "id": "retired-destruction",
"metadata": {},
"source": [
"Or you can use a **relative import**, where `..` refers to the package one level up. From within `mod1.py`, which is in subpackage `sub_pkg`,\n",
@@ -1069,7 +1015,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "ahead-mobility",
"metadata": {},
"outputs": [],
"source": [
@@ -1079,7 +1024,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "designed-electricity",
"metadata": {},
"outputs": [
{
@@ -1102,7 +1046,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "smaller-disposal",
"metadata": {},
"outputs": [
{
@@ -1120,16 +1063,15 @@
},
{
"cell_type": "markdown",
- "id": "initial-liquid",
"metadata": {},
"source": [
"\n",
- "## Summary"
+ "## Summary [
](#table_of_contents)\n",
+ "\n"
]
},
{
"cell_type": "markdown",
- "id": "single-closure",
"metadata": {},
"source": [
"In modularization section, the following topics are covered:\n",
@@ -1159,7 +1101,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/08. The Meaning of Underscore (_).ipynb b/Python/04. Advanced/01. The Meaning of Underscore (_).ipynb
similarity index 78%
rename from python/04. Advanced/08. The Meaning of Underscore (_).ipynb
rename to Python/04. Advanced/01. The Meaning of Underscore (_).ipynb
index 86d40aa..cef653d 100755
--- a/python/04. Advanced/08. The Meaning of Underscore (_).ipynb
+++ b/Python/04. Advanced/01. The Meaning of Underscore (_).ipynb
@@ -4,8 +4,36 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# 07.3 The Meaning of Underscore (`_`)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
07.3 The Meaning of Underscore (`_`) \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "##
Table of Contents\n",
+ "* [1. Single Leading Underscore: `_var`](#1._single_leading_underscore:_`_var`)\n",
+ "* [2. Single Trailing Underscore: `var_`](#2._single_trailing_underscore:_`var_`)\n",
+ "* [3. Double Leading Underscore: `__var`](#3._double_leading_underscore:_`__var`)\n",
+ "* [4. Double Leading and Trailing Underscore: `__var__`](#4._double_leading_and_trailing_underscore:_`__var__`)\n",
+ "* [5. Single Underscore: `_`](#5._single_underscore:_`_`)\n",
+ "* [Summary](#summary)\n",
"\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
"Single and double underscores have a meaning in Python variable and method names. Some of that meaning is merely by convention and intended as a hint to the programmer—and some of it is enforced by the Python interpreter.\n",
"\n",
"- Single Leading Underscore: `_var`\n",
@@ -19,7 +47,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## 1. Single Leading Underscore: `_var`\n",
+ "\n",
+ "## 1. Single Leading Underscore: `_var` [
](#table_of_contents)\n",
"\n",
"When it comes to variable and method names, the single underscore prefix has a meaning by convention only. It’s a hint to the programmer—and it means what the Python community agrees it should mean, but it does not affect the behavior of your programs."
]
@@ -42,7 +71,7 @@
},
{
"cell_type": "code",
- "execution_count": 39,
+ "execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
@@ -54,7 +83,7 @@
},
{
"cell_type": "code",
- "execution_count": 40,
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
@@ -63,7 +92,7 @@
"(11, 23)"
]
},
- "execution_count": 40,
+ "execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
@@ -139,7 +168,6 @@
"metadata": {},
"source": [
"If you stick to the PEP 8 recommendation that wildcard imports should be avoided, then really all you need to remember is this:\n",
- "\n",
"> _Single underscores are a Python naming convention indicating a name is meant for internal use. It is generally not enforced by the Python interpreter and meant as a hint to the programmer only._"
]
},
@@ -147,7 +175,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## 2. Single Trailing Underscore: `var_`"
+ "\n",
+ "## 2. Single Trailing Underscore: `var_` [
](#table_of_contents)"
]
},
{
@@ -159,15 +188,15 @@
},
{
"cell_type": "code",
- "execution_count": 45,
+ "execution_count": 27,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
- "evalue": "invalid syntax (, line 1)",
+ "evalue": "invalid syntax (295413822.py, line 1)",
"output_type": "error",
"traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m def make_object(name, class):\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
+ "\u001b[0;36m File \u001b[0;32m\"/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_24177/295413822.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m def make_object(name, class):\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
]
}
],
@@ -178,7 +207,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
@@ -197,7 +226,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## 3. Double Leading Underscore: `__var`"
+ "\n",
+ "## 3. Double Leading Underscore: `__var` [
](#table_of_contents)"
]
},
{
@@ -214,19 +244,6 @@
"A double underscore prefix causes the Python interpreter to rewrite the attribute name in order to avoid naming conflicts in subclasses. This is also called name _mangling_ —the interpreter changes the name of the variable in a way that makes it harder to create collisions when the class is extended later."
]
},
- {
- "cell_type": "code",
- "execution_count": 80,
- "metadata": {},
- "outputs": [],
- "source": [
- "class Test:\n",
- " def __init__(self):\n",
- " self.foo = 11\n",
- " self._bar = 23\n",
- " self.__baz = 23"
- ]
- },
{
"cell_type": "markdown",
"metadata": {},
@@ -236,7 +253,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 48,
"metadata": {},
"outputs": [
{
@@ -273,7 +290,7 @@
" 'foo']"
]
},
- "execution_count": 18,
+ "execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
@@ -489,7 +506,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -503,7 +520,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 79,
"metadata": {},
"outputs": [
{
@@ -513,7 +530,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mMangledMethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_24177/2847256654.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mMangledMethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m: 'MangledMethod' object has no attribute '__method'"
]
}
@@ -524,7 +541,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 78,
"metadata": {},
"outputs": [
{
@@ -533,7 +550,7 @@
"42"
]
},
- "execution_count": 19,
+ "execution_count": 78,
"metadata": {},
"output_type": "execute_result"
}
@@ -546,7 +563,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## 4. Double Leading and Trailing Underscore: `__var__`"
+ "\n",
+ "## 4. Double Leading and Trailing Underscore: `__var__` [
](#table_of_contents)"
]
},
{
@@ -598,7 +616,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## 5. Single Underscore: `_`"
+ "\n",
+ "## 5. Single Underscore: `_` [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Temporary Variable [
](#table_of_contents)"
]
},
{
@@ -653,25 +679,7 @@
},
{
"cell_type": "code",
- "execution_count": 109,
- "metadata": {},
- "outputs": [],
- "source": [
- "row = (\"ali\", \"hejazi\", 28, \"Canada\", \"Edmonton\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 120,
- "metadata": {},
- "outputs": [],
- "source": [
- "name, family_name, _, _, _ = row"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 122,
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -681,7 +689,7 @@
},
{
"cell_type": "code",
- "execution_count": 123,
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
@@ -690,7 +698,7 @@
"'red'"
]
},
- "execution_count": 123,
+ "execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@@ -701,8 +709,10 @@
},
{
"cell_type": "code",
- "execution_count": 124,
- "metadata": {},
+ "execution_count": 8,
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
@@ -710,7 +720,7 @@
"3812.4"
]
},
- "execution_count": 124,
+ "execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@@ -721,7 +731,7 @@
},
{
"cell_type": "code",
- "execution_count": 125,
+ "execution_count": 9,
"metadata": {},
"outputs": [
{
@@ -730,7 +740,7 @@
"12"
]
},
- "execution_count": 125,
+ "execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@@ -739,9 +749,17 @@
"_"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Single Underscores in Numeric Literals [
](#table_of_contents)\n",
+ "Python allows you to put underscores in numbers for convenience. They're used to separate groups of numbers, much like commas do in non-programming. Underscores are completely ignored in numbers, much like comments. So this:"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 127,
+ "execution_count": 1,
"metadata": {},
"outputs": [
{
@@ -750,7 +768,7 @@
"3000000000"
]
},
- "execution_count": 127,
+ "execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
@@ -763,7 +781,39 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Summary"
+ "### Single Underscores to represent the last expression in the interpreter [
](#table_of_contents)\n",
+ "According to [Python doc](https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers), the special identifier `_` is used in the interactive interpreter to store the result of the last evaluation. It is stored in the builtin module."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here is an example. At first, we check that `_` is not stored in the builtin module, then we write a single expression without a variable name. If we check the builtin module again, we will find `_` in the module and the value is the last evaluation."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```python\n",
+ ">>> '_' in dir(__builtins__)\n",
+ "False\n",
+ ">>> 1+1\n",
+ "2\n",
+ ">>> '_' in dir(__builtins__)\n",
+ "True\n",
+ ">>> _\n",
+ "2\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Summary [
](#table_of_contents)"
]
},
{
@@ -776,7 +826,7 @@
"|Single Trailing Underscore|`var_`|Used by convention to avoid naming conflicts with Python keywords.|\n",
"|Double Leading Underscore|`__var`|Triggers name mangling when used in a class context. Enforced by the Python interpreter.|\n",
"|Double Leading and Trailing Underscore|`__var__`|Indicates special methods defined by the Python language. Avoid this naming scheme for your own attributes.|\n",
- "|Single Underscore|`_`|Sometimes used as a name for temporary or insignificant variables (“don’t care”). Also: The result of the last expression in a Python REPL.|"
+ "|Single Underscore|`_`|Sometimes used as a name for temporary or insignificant variables (“don’t care”). Also a separator for groups of numbers and the result of the last expression in a Python REPL.|"
]
},
{
@@ -803,7 +853,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/09. Error Handling.ipynb b/Python/04. Advanced/02. Error Handling.ipynb
similarity index 80%
rename from python/04. Advanced/09. Error Handling.ipynb
rename to Python/04. Advanced/02. Error Handling.ipynb
index 73c1953..9209c1f 100755
--- a/python/04. Advanced/09. Error Handling.ipynb
+++ b/Python/04. Advanced/02. Error Handling.ipynb
@@ -2,10 +2,16 @@
"cells": [
{
"cell_type": "markdown",
- "id": "instructional-accused",
"metadata": {},
"source": [
- "# Error Handling (Exceptions)\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Error Handling (Exceptions) \n",
"\n",
"A Python program terminates as soon as it encounters an error. In Python, an error can be a syntax error or an exception.\n",
"\n",
@@ -14,26 +20,52 @@
},
{
"cell_type": "markdown",
- "id": "secret-permit",
"metadata": {},
"source": [
- "## Exceptions versus Syntax Errors\n",
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Exceptions versus Syntax Errors](#exceptions_versus_syntax_errors)\n",
+ "* [Raising an Exception](#raising_an_exception)\n",
+ "* [The `AssertionError` Exception](#the_`assertionerror`_exception)\n",
+ "* [The `try` and `except` Block: Handling Exceptions](#the_`try`_and_`except`_block:_handling_exceptions)\n",
+ " * [The `except` Clause with No Exceptions](#the_`except`_clause_with_no_exceptions)\n",
+ " * [The `except` Clause with Multiple Exceptions](#the_`except`_clause_with_multiple_exceptions)\n",
+ "* [The else Clause](#the_else_clause)\n",
+ "* [Cleaning Up After Using `finally`](#cleaning_up_after_using_`finally`)\n",
+ "* [List of Some Standard Exceptions](#list_of_some_standard_exceptions)\n",
+ "* [Define Custom Exception](#define_custom_exception)\n",
+ " * [Creating Custom Exceptions](#creating_custom_exceptions)\n",
+ " * [Example: User-Defined Exception in Python](#example:_user-defined_exception_in_python)\n",
+ " * [Customizing Exception Classes](#customizing_exception_classes)\n",
+ "* [Summary](#summary)\n",
+ "* [References](#references)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Exceptions versus Syntax Errors [
](#table_of_contents)\n",
"\n",
"Syntax errors occur when the parser detects an incorrect statement. Observe the following example:"
]
},
{
"cell_type": "code",
- "execution_count": 71,
- "id": "answering-metadata",
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
- "evalue": "invalid syntax (, line 1)",
+ "evalue": "invalid syntax (439779720.py, line 1)",
"output_type": "error",
"traceback": [
- "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(0 / 0))\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
+ "\u001b[0;36m File \u001b[0;32m\"/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_79930/439779720.py\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(0 / 0))\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
]
}
],
@@ -43,7 +75,6 @@
},
{
"cell_type": "markdown",
- "id": "floppy-passion",
"metadata": {},
"source": [
"The arrow indicates where the parser ran into the syntax error. In this example, there was one bracket too many. Remove it and run your code again:"
@@ -51,8 +82,7 @@
},
{
"cell_type": "code",
- "execution_count": 72,
- "id": "divine-senior",
+ "execution_count": 9,
"metadata": {},
"outputs": [
{
@@ -62,18 +92,17 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_79930/186921146.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mZeroDivisionError\u001b[0m: division by zero"
]
}
],
"source": [
- "print(0 / 0)"
+ "print(10 / 0)"
]
},
{
"cell_type": "markdown",
- "id": "established-costume",
"metadata": {},
"source": [
"The arrow indicates where the parser ran into the syntax error. In this example, there was one bracket too many. Remove it and run your code again:"
@@ -81,7 +110,6 @@
},
{
"cell_type": "markdown",
- "id": "tough-while",
"metadata": {},
"source": [
"Instead of showing the message exception `error`, Python details what type of exception error was encountered. In this case, it was a `ZeroDivisionError`. Python comes with various built-in exceptions as well as the possibility to create self-defined exceptions."
@@ -89,20 +117,42 @@
},
{
"cell_type": "markdown",
- "id": "experienced-copying",
"metadata": {},
"source": [
- "## Raising an Exception\n",
+ "\n",
+ "## Raising an Exception [
](#table_of_contents)\n",
"\n",
"We can use raise to throw an exception if a condition occurs. The statement can be complemented with a custom exception.\n",
"\n",
"If you want to throw an error when a certain condition occurs using raise, you could go about it like this:"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "Exception",
+ "evalue": "X is not allowed to be greater that 5!",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mException\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_61944/4181305022.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"X is not allowed to be greater that 5!\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mException\u001b[0m: X is not allowed to be greater that 5!"
+ ]
+ }
+ ],
+ "source": [
+ "x = 10\n",
+ "if x > 5:\n",
+ " raise Exception(\"X is not allowed to be greater that 5!\")"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 78,
- "id": "active-player",
"metadata": {},
"outputs": [
{
@@ -125,7 +175,6 @@
},
{
"cell_type": "markdown",
- "id": "laughing-seller",
"metadata": {},
"source": [
"The program comes to a halt and displays our exception to screen, offering clues about what went wrong."
@@ -133,15 +182,44 @@
},
{
"cell_type": "markdown",
- "id": "amazing-light",
"metadata": {},
"source": [
- "## The `AssertionError` Exception"
+ "\n",
+ "## The `AssertionError` Exception [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = 5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "AssertionError",
+ "evalue": "x should be equal to 4",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_61944/2648026492.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"x should be equal to 4\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mAssertionError\u001b[0m: x should be equal to 4"
+ ]
+ }
+ ],
+ "source": [
+ "assert x == 4, \"x should be equal to 4\""
]
},
{
"cell_type": "markdown",
- "id": "active-moore",
"metadata": {},
"source": [
"Instead of waiting for a program to crash midway, you can also start by making an assertion in Python. We `assert` that a certain condition is met. If this condition turns out to be `True`, then that is excellent! The program can continue. If the condition turns out to be False, you can have the program throw an `AssertionError` exception.\n",
@@ -152,7 +230,6 @@
{
"cell_type": "code",
"execution_count": 94,
- "id": "enabling-documentation",
"metadata": {},
"outputs": [],
"source": [
@@ -162,7 +239,6 @@
},
{
"cell_type": "markdown",
- "id": "sublime-theology",
"metadata": {},
"source": [
"f you run this code on a Linux machine, the assertion passes. If you were to run this code on a Windows machine, the outcome of the assertion would be `False` and the result would be the following:\n",
@@ -176,7 +252,6 @@
},
{
"cell_type": "markdown",
- "id": "realistic-indicator",
"metadata": {},
"source": [
"In this example, throwing an AssertionError exception is the last thing that the program will do. The program will come to halt and will not continue. What if that is not what you want?"
@@ -184,10 +259,10 @@
},
{
"cell_type": "markdown",
- "id": "magnetic-investigation",
"metadata": {},
"source": [
- "## The `try` and `except` Block: Handling Exceptions\n",
+ "\n",
+ "## The `try` and `except` Block: Handling Exceptions [
](#table_of_contents)\n",
"\n",
"The `try` and `except` block in Python is used to catch and handle `exceptions`. Python executes code following the try statement as a _normal_ part of the program. The code that follows the `except` statement is the program’s response to any exceptions in the preceding `try` clause.\n",
"\n",
@@ -196,7 +271,6 @@
},
{
"cell_type": "markdown",
- "id": "psychological-budapest",
"metadata": {},
"source": [
"As you saw earlier, when syntactically correct code runs into an `error`, Python will throw an exception `error`. This exception error will crash the program if it is unhandled. The `except` clause determines how your program responds to exceptions.\n",
@@ -206,8 +280,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
- "id": "resident-graduation",
+ "execution_count": 47,
"metadata": {},
"outputs": [],
"source": [
@@ -218,7 +291,6 @@
},
{
"cell_type": "markdown",
- "id": "passive-dispatch",
"metadata": {},
"source": [
"The `linux_interaction()` can only run on a Linux system. The `assert` in this function will throw an `AssertionError` exception if you call it on an operating system other then Linux.\n",
@@ -228,18 +300,9 @@
},
{
"cell_type": "code",
- "execution_count": 25,
- "id": "suburban-active",
+ "execution_count": 50,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Doing something.\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"try:\n",
" linux_interaction()\n",
@@ -249,7 +312,6 @@
},
{
"cell_type": "markdown",
- "id": "atomic-better",
"metadata": {},
"source": [
"The way you handled the error here is by handing out a pass. If you were to run this code on a Windows machine, you would get nothing.\n",
@@ -259,15 +321,14 @@
},
{
"cell_type": "code",
- "execution_count": 9,
- "id": "earlier-allah",
+ "execution_count": 51,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Doing something.\n"
+ "Linux function was not executed\n"
]
}
],
@@ -280,7 +341,6 @@
},
{
"cell_type": "markdown",
- "id": "british-unknown",
"metadata": {},
"source": [
"Execute this code on a Windows machine, you would get this as output:\n",
@@ -292,7 +352,6 @@
},
{
"cell_type": "markdown",
- "id": "suited-intranet",
"metadata": {},
"source": [
"When an exception occurs in a program running this function, the program will continue as well as inform you about the fact that the function call was not successful.\n",
@@ -305,7 +364,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "thrown-cartridge",
"metadata": {},
"outputs": [
{
@@ -326,7 +384,6 @@
},
{
"cell_type": "markdown",
- "id": "norman-operator",
"metadata": {},
"source": [
"Running this function on a Windows machine outputs the following:\n",
@@ -339,7 +396,6 @@
},
{
"cell_type": "markdown",
- "id": "reported-browse",
"metadata": {},
"source": [
"The first message is the `AssertionError`, informing you that the function can only be executed on a Linux machine. The second message tells you which function was not executed.\n",
@@ -349,7 +405,6 @@
},
{
"cell_type": "markdown",
- "id": "lesser-point",
"metadata": {},
"source": [
"Here’s another example where you open a file and use a built-in exception:"
@@ -358,7 +413,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "typical-technique",
"metadata": {},
"outputs": [
{
@@ -379,7 +433,6 @@
},
{
"cell_type": "markdown",
- "id": "threatened-midwest",
"metadata": {},
"source": [
"If file.log does not exist, this block of code will output the following:\n",
@@ -391,7 +444,6 @@
},
{
"cell_type": "markdown",
- "id": "lesser-friendship",
"metadata": {},
"source": [
"This is an informative message, and our program will still continue to run. In the [Python docs](https://docs.python.org/3/library/exceptions.html), you can see that there are a lot of built-in exceptions that you can use here. One exception described on that page is the following:\n",
@@ -405,8 +457,7 @@
},
{
"cell_type": "code",
- "execution_count": 35,
- "id": "continent-textbook",
+ "execution_count": 65,
"metadata": {},
"outputs": [
{
@@ -427,7 +478,6 @@
},
{
"cell_type": "markdown",
- "id": "cleared-corner",
"metadata": {},
"source": [
"In this case, if file.log does not exist, the output will be the following:\n",
@@ -439,7 +489,6 @@
},
{
"cell_type": "markdown",
- "id": "victorian-grave",
"metadata": {},
"source": [
"You can have more than one function call in your `try` clause and anticipate catching various exceptions. A thing to note here is that the code in the `try` clause will stop as soon as an exception is encountered."
@@ -447,7 +496,6 @@
},
{
"cell_type": "markdown",
- "id": "geological-package",
"metadata": {},
"source": [
"Look at the following code. Here, you first call the `linux_interaction()` function and then try to open a file"
@@ -456,7 +504,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "qualified-speed",
"metadata": {},
"outputs": [
{
@@ -482,7 +529,6 @@
},
{
"cell_type": "markdown",
- "id": "acknowledged-liver",
"metadata": {},
"source": [
"If the file does not exist, running this code on a Windows machine will output the following:\n",
@@ -495,7 +541,6 @@
},
{
"cell_type": "markdown",
- "id": "pharmaceutical-jumping",
"metadata": {},
"source": [
"Inside the `try` clause, you ran into an exception immediately and did not get to the part where you attempt to open `file.log`. Now look at what happens when you run the code on a Linux machine:\n",
@@ -507,7 +552,6 @@
},
{
"cell_type": "markdown",
- "id": "introductory-trash",
"metadata": {},
"source": [
"Here are the key takeaways:\n",
@@ -520,10 +564,10 @@
},
{
"cell_type": "markdown",
- "id": "fancy-insured",
"metadata": {},
"source": [
- "### The `except` Clause with No Exceptions\n",
+ "\n",
+ "### The `except` Clause with No Exceptions [
](#table_of_contents)\n",
"\n",
"You can also use the except statement with no exceptions defined as follows:"
]
@@ -531,7 +575,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "hairy-trading",
"metadata": {},
"outputs": [
{
@@ -552,7 +595,6 @@
},
{
"cell_type": "markdown",
- "id": "confirmed-adobe",
"metadata": {},
"source": [
"This kind of a try-except statement catches all the exceptions that occur. Using this kind of try-except statement is not considered a good programming practice though, because it catches all exceptions but does not make the programmer identify the root cause of the problem that may occur."
@@ -560,7 +602,6 @@
},
{
"cell_type": "markdown",
- "id": "moderate-limitation",
"metadata": {},
"source": [
"**Warning:** Catching `Exception` hides all errors…even those which are completely unexpected. This is why you should avoid bare `except` clauses in your Python programs. Instead, you’ll want to refer to _specific exception classes_ you want to catch and handle. You can learn more about why this is a good idea in [this tutorial](https://realpython.com/the-most-diabolical-python-antipattern/)."
@@ -568,10 +609,10 @@
},
{
"cell_type": "markdown",
- "id": "positive-parish",
"metadata": {},
"source": [
- "### The `except` Clause with Multiple Exceptions\n",
+ "\n",
+ "### The `except` Clause with Multiple Exceptions [
](#table_of_contents)\n",
"\n",
"You can also use the same except statement to handle multiple exceptions as follows:"
]
@@ -579,7 +620,6 @@
{
"cell_type": "code",
"execution_count": 131,
- "id": "native-auction",
"metadata": {},
"outputs": [
{
@@ -600,10 +640,10 @@
},
{
"cell_type": "markdown",
- "id": "failing-anderson",
"metadata": {},
"source": [
- "## The else Clause\n",
+ "\n",
+ "## The else Clause [
](#table_of_contents)\n",
"\n",
"In Python, using the `else` statement, you can instruct a program to execute a certain block of code only in the absence of exceptions.\n",
"\n",
@@ -612,7 +652,6 @@
},
{
"cell_type": "markdown",
- "id": "sorted-senior",
"metadata": {},
"source": [
"Look at the following example:"
@@ -621,7 +660,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "another-metadata",
"metadata": {},
"outputs": [
{
@@ -644,7 +682,6 @@
},
{
"cell_type": "markdown",
- "id": "incredible-hughes",
"metadata": {},
"source": [
"If you were to run this code on a Linux system, the output would be the following:\n",
@@ -657,7 +694,6 @@
},
{
"cell_type": "markdown",
- "id": "quality-wisdom",
"metadata": {},
"source": [
"Because the program did not run into any exceptions, the `else` clause was executed.\n",
@@ -668,7 +704,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "alive-marshall",
"metadata": {},
"outputs": [
{
@@ -695,7 +730,6 @@
},
{
"cell_type": "markdown",
- "id": "little-distribution",
"metadata": {},
"source": [
"If you were to execute this code on a Linux machine, you would get the following result:\n",
@@ -708,7 +742,6 @@
},
{
"cell_type": "markdown",
- "id": "shaped-hypothetical",
"metadata": {},
"source": [
"From the output, you can see that the `linux_interaction()` function ran. Because no exceptions were encountered, an attempt to open `file.log` was made. That file did not exist, and instead of opening the file, you caught the `FileNotFoundError` exception."
@@ -716,10 +749,10 @@
},
{
"cell_type": "markdown",
- "id": "express-clinic",
"metadata": {},
"source": [
- "## Cleaning Up After Using `finally`\n",
+ "\n",
+ "## Cleaning Up After Using `finally` [
](#table_of_contents)\n",
"\n",
"Imagine that you always had to implement some sort of action to clean up after executing your code. Python enables you to do so using the finally clause.\n",
"\n",
@@ -728,7 +761,6 @@
},
{
"cell_type": "markdown",
- "id": "smooth-behavior",
"metadata": {},
"source": [
"Have a look at the following example:"
@@ -737,7 +769,6 @@
{
"cell_type": "code",
"execution_count": 141,
- "id": "derived-disorder",
"metadata": {},
"outputs": [
{
@@ -767,7 +798,6 @@
},
{
"cell_type": "markdown",
- "id": "controlled-fundamental",
"metadata": {},
"source": [
"In the previous code, everything in the finally clause will be executed. It does not matter if you encounter an exception somewhere in the `try` or `else` clauses. Running the previous code on a Windows machine would output the following:\n",
@@ -780,15 +810,14 @@
},
{
"cell_type": "markdown",
- "id": "abroad-phase",
"metadata": {},
"source": [
- "## List of Some Standard Exceptions"
+ "\n",
+ "## List of Some Standard Exceptions [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "serial-climb",
"metadata": {},
"source": [
"|Exception|Description|\n",
@@ -826,10 +855,10 @@
},
{
"cell_type": "markdown",
- "id": "catholic-carnival",
"metadata": {},
"source": [
- "## Define Custom Exception\n",
+ "\n",
+ "## Define Custom Exception [
](#table_of_contents)\n",
"\n",
"Python has numerous built-in exceptions that force your program to output an error when something in the program goes wrong.\n",
"\n",
@@ -838,18 +867,17 @@
},
{
"cell_type": "markdown",
- "id": "vertical-nicaragua",
"metadata": {},
"source": [
- "### Creating Custom Exceptions\n",
+ "\n",
+ "### Creating Custom Exceptions [
](#table_of_contents)\n",
"\n",
"In Python, users can define custom exceptions by creating a new class. This exception class has to be derived, either directly or indirectly, from the built-in `Exception` class. Most of the built-in exceptions are also derived from this class."
]
},
{
"cell_type": "code",
- "execution_count": 147,
- "id": "virtual-luther",
+ "execution_count": 103,
"metadata": {},
"outputs": [],
"source": [
@@ -859,7 +887,6 @@
},
{
"cell_type": "markdown",
- "id": "killing-microphone",
"metadata": {},
"source": [
"Here, we have created a user-defined exception called `CustomError` which inherits from the `Exception` class. This new exception, like other exceptions, can be raised using the `raise` statement with an optional error message."
@@ -867,8 +894,7 @@
},
{
"cell_type": "code",
- "execution_count": 148,
- "id": "adjustable-module",
+ "execution_count": 104,
"metadata": {},
"outputs": [
{
@@ -878,7 +904,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mCustomError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mCustomError\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_61944/571119069.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mCustomError\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mCustomError\u001b[0m: "
]
}
@@ -889,8 +915,7 @@
},
{
"cell_type": "code",
- "execution_count": 149,
- "id": "stretch-withdrawal",
+ "execution_count": 105,
"metadata": {},
"outputs": [
{
@@ -900,7 +925,7 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mCustomError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mCustomError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"An error occurred\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_61944/4012702173.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mCustomError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"An error occurred\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mCustomError\u001b[0m: An error occurred"
]
}
@@ -911,7 +936,6 @@
},
{
"cell_type": "markdown",
- "id": "rising-facility",
"metadata": {},
"source": [
"When we are developing a large Python program, it is a good practice to place all the user-defined exceptions that our program raises in a separate file. Many standard modules do this. They define their exceptions separately as `exceptions.py` or `errors.py` (generally but not always). Look [here](https://github.com/RasaHQ/rasa/blob/main/rasa/shared/exceptions.py) for a real example."
@@ -919,7 +943,6 @@
},
{
"cell_type": "markdown",
- "id": "hearing-convergence",
"metadata": {},
"source": [
"User-defined exception class can implement everything a normal class can do, but we generally make them simple and concise. Most implementations declare a custom base class and derive others exception classes from this base class. This concept is made clearer in the following example."
@@ -927,17 +950,16 @@
},
{
"cell_type": "markdown",
- "id": "wound-proof",
"metadata": {},
"source": [
- "### Example: User-Defined Exception in Python\n",
+ "\n",
+ "### Example: User-Defined Exception in Python [
](#table_of_contents)\n",
"\n",
"In this example, we will illustrate how user-defined exceptions can be used in a program to raise and catch errors."
]
},
{
"cell_type": "markdown",
- "id": "further-breath",
"metadata": {},
"source": [
"This program will ask the user to enter a number until they guess a stored number correctly. To help them figure it out, a hint is provided whether their guess is greater than or less than the stored number"
@@ -945,8 +967,7 @@
},
{
"cell_type": "code",
- "execution_count": 152,
- "id": "adaptive-birthday",
+ "execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
@@ -963,25 +984,9 @@
},
{
"cell_type": "code",
- "execution_count": 41,
- "id": "median-bridal",
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Enter a number: 5\n",
- "This value is too small, try again!\n",
- "\n",
- "Enter a number: 11\n",
- "This value is too large, try again!\n",
- "\n",
- "Enter a number: 10\n",
- "Congratulations! You guessed it correctly.\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"# you need to guess this number\n",
"number = 10\n",
@@ -1007,7 +1012,6 @@
},
{
"cell_type": "markdown",
- "id": "unauthorized-generic",
"metadata": {},
"source": [
"We have defined a base class called `Error`.\n",
@@ -1017,10 +1021,10 @@
},
{
"cell_type": "markdown",
- "id": "cellular-twelve",
"metadata": {},
"source": [
- "### Customizing Exception Classes\n",
+ "\n",
+ "### Customizing Exception Classes [
](#table_of_contents)\n",
"\n",
"We can further customize this class to accept other arguments as per our needs."
]
@@ -1028,7 +1032,6 @@
{
"cell_type": "code",
"execution_count": 157,
- "id": "early-distinction",
"metadata": {},
"outputs": [],
"source": [
@@ -1049,7 +1052,6 @@
{
"cell_type": "code",
"execution_count": 158,
- "id": "acute-idaho",
"metadata": {},
"outputs": [
{
@@ -1079,7 +1081,6 @@
},
{
"cell_type": "markdown",
- "id": "quick-information",
"metadata": {},
"source": [
"Here, we have overridden the constructor of the `Exception` class to accept our own custom arguments `salary` and `message`. Then, the constructor of the parent `Exception` class is called manually with the `self.message` argument using `super()`."
@@ -1087,7 +1088,6 @@
},
{
"cell_type": "markdown",
- "id": "periodic-methodology",
"metadata": {},
"source": [
"The inherited `__str__` method of the Exception class is then used to display the corresponding message when `SalaryNotInRangeError` is raised. We can also customize the `__str__` method itself by overriding it."
@@ -1096,7 +1096,6 @@
{
"cell_type": "code",
"execution_count": 167,
- "id": "ready-diabetes",
"metadata": {},
"outputs": [],
"source": [
@@ -1120,7 +1119,6 @@
{
"cell_type": "code",
"execution_count": 168,
- "id": "associate-attendance",
"metadata": {},
"outputs": [
{
@@ -1150,10 +1148,10 @@
},
{
"cell_type": "markdown",
- "id": "indian-christopher",
"metadata": {},
"source": [
- "## Summary\n",
+ "\n",
+ "## Summary [
](#table_of_contents)\n",
"\n",
"- `raise` allows you to throw an exception at any time.\n",
"- `assert` enables you to verify if a certain condition is met and throw an exception if it isn’t.\n",
@@ -1166,10 +1164,10 @@
},
{
"cell_type": "markdown",
- "id": "prescription-bidding",
"metadata": {},
"source": [
- "## References\n",
+ "\n",
+ "## References [
](#table_of_contents)\n",
"- https://realpython.com/python-exceptions/\n",
"- https://www.programiz.com/python-programming/user-defined-exception"
]
@@ -1191,7 +1189,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/10.1 Decorators.ipynb b/Python/04. Advanced/03.1 Decorators.ipynb
similarity index 88%
rename from python/04. Advanced/10.1 Decorators.ipynb
rename to Python/04. Advanced/03.1 Decorators.ipynb
index 5e11339..e095caa 100755
--- a/python/04. Advanced/10.1 Decorators.ipynb
+++ b/Python/04. Advanced/03.1 Decorators.ipynb
@@ -2,29 +2,53 @@
"cells": [
{
"cell_type": "markdown",
- "id": "cellular-peeing",
"metadata": {},
"source": [
- "# Decorators\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "#
Decorators \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
"\n",
- "* [1. Inner Functions](#inner_functions)\n",
- "* [2. Returning Functions from Functions](#returning_functions_from_functions)\n",
- "* [3. Simple Decorators](#simple_decorators)\n",
- "* [4. Syntactic Sugar](#syntactic_sugar)\n",
- "* [5. Reusing Decorators](#reusing_decorators)\n",
- "* [6. Decorating Functions with Arguments](#decorating_functions_with_arguments)\n",
- "* [7. Returning Values From Decorated Functions](#returning_values_from_decorated_functions)\n",
- "* [8. Who Are You, Really?](#who_are_you_really)\n",
- "* [9. A Few Real World Examples](#real_world_examples)\n",
"\n",
- "By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.\n",
+ "* [Inner Functions](#inner_functions)\n",
+ "* [Returning Functions From Functions](#returning_functions_from_functions)\n",
+ "* [Simple Decorators](#simple_decorators)\n",
+ "* [Syntactic Sugar!](#syntactic_sugar!)\n",
+ "* [Reusing Decorators](#reusing_decorators)\n",
+ "* [Decorating Functions with Arguments](#decorating_functions_with_arguments)\n",
+ "* [Returning Values From Decorated Functions](#returning_values_from_decorated_functions)\n",
+ "* [Who Are You, Really?](#who_are_you,_really?)\n",
+ "* [A Few Real World Examples](#a_few_real_world_examples)\n",
+ " * [Timing Functions](#timing_functions)\n",
+ " * [Debugging Code](#debugging_code)\n",
+ " * [Slowing Down Code](#slowing_down_code)\n",
"\n",
- "This sounds confusing, but it’s really not, especially after you’ve seen a few examples of how decorators work."
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it."
]
},
{
"cell_type": "markdown",
- "id": "exposed-partnership",
"metadata": {},
"source": [
"In Python, functions are [first-class objects](https://dbader.org/blog/python-first-class-functions). This means that functions can be passed around and used as arguments, just like any other object (string, int, float, list, and so on). Consider the following three functions:"
@@ -33,7 +57,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "above-steel",
"metadata": {},
"outputs": [],
"source": [
@@ -49,7 +72,6 @@
},
{
"cell_type": "markdown",
- "id": "ancient-productivity",
"metadata": {},
"source": [
"Here, `say_hello()` and `be_awesome()` are regular functions that expect a name given as a string. The `greet_bob()` function however, expects a function as its argument. We can, for instance, pass it the `say_hello()` or the `be_awesome()` function:"
@@ -58,7 +80,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "executed-power",
"metadata": {},
"outputs": [
{
@@ -79,7 +100,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "bacterial-credits",
"metadata": {},
"outputs": [
{
@@ -99,11 +119,10 @@
},
{
"cell_type": "markdown",
- "id": "convinced-binary",
"metadata": {},
"source": [
"\n",
- "## Inner Functions\n",
+ "## Inner Functions [
](#table_of_contents)\n",
"\n",
"It’s possible to define functions inside other functions. Such functions are called [inner functions](https://realpython.com/inner-functions-what-are-they-good-for/). Here’s an example of a function with two inner functions:"
]
@@ -111,7 +130,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "monthly-battle",
"metadata": {},
"outputs": [],
"source": [
@@ -131,7 +149,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "ahead-adapter",
"metadata": {},
"outputs": [
{
@@ -150,7 +167,6 @@
},
{
"cell_type": "markdown",
- "id": "persistent-pregnancy",
"metadata": {},
"source": [
"Note that the order in which the inner functions are defined does not matter. Like with any other functions, the printing only happens when the inner functions are executed.\n",
@@ -161,7 +177,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "built-genius",
"metadata": {},
"outputs": [
{
@@ -182,7 +197,6 @@
},
{
"cell_type": "markdown",
- "id": "moderate-frost",
"metadata": {},
"source": [
"Whenever you call `parent()`, the inner functions `first_child()` and `second_child()` are also called. But because of their local scope, they aren’t available outside of the `parent()` function."
@@ -190,16 +204,14 @@
},
{
"cell_type": "markdown",
- "id": "centered-width",
"metadata": {},
"source": [
"\n",
- "## Returning Functions From Functions"
+ "## Returning Functions From Functions [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "ranging-fifteen",
"metadata": {},
"source": [
"Python also allows you to use functions as return values. The following example returns one of the inner functions from the outer `parent()` function:"
@@ -208,7 +220,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "sacred-cedar",
"metadata": {},
"outputs": [],
"source": [
@@ -228,7 +239,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "developmental-diversity",
"metadata": {},
"outputs": [
{
@@ -248,7 +258,6 @@
},
{
"cell_type": "markdown",
- "id": "heard-frequency",
"metadata": {},
"source": [
"Note that you are returning `first_child` without the parentheses. Recall that this means that you are returning a reference to the function `first_child`. "
@@ -256,19 +265,17 @@
},
{
"cell_type": "markdown",
- "id": "little-belarus",
"metadata": {},
"source": [
"\n",
- "## Simple Decorators\n",
+ "## Simple Decorators [
](#table_of_contents)\n",
"\n",
"Now that you’ve seen that functions are just like any other object in Python, you’re ready to move on and see the magical beast that is the Python decorator. Let’s start with an example:"
]
},
{
"cell_type": "code",
- "execution_count": 19,
- "id": "extraordinary-marshall",
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -287,7 +294,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "fiscal-salad",
"metadata": {},
"outputs": [],
"source": [
@@ -305,7 +311,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "nearby-disposition",
"metadata": {},
"outputs": [],
"source": [
@@ -314,7 +319,6 @@
},
{
"cell_type": "markdown",
- "id": "activated-burlington",
"metadata": {},
"source": [
"Can you guess what happens when you call `say_whee()`? Try it:"
@@ -323,7 +327,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "constant-freedom",
"metadata": {},
"outputs": [
{
@@ -342,7 +345,6 @@
},
{
"cell_type": "markdown",
- "id": "administrative-terrorism",
"metadata": {},
"source": [
"The so-called decoration happens at the following line:\n",
@@ -354,7 +356,6 @@
},
{
"cell_type": "markdown",
- "id": "grave-population",
"metadata": {},
"source": [
"In effect, the name say_whee now points to the `wrapper()` inner function. Remember that you return wrapper as a function when you call `my_decorator(say_whee)`:"
@@ -363,7 +364,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "stretch-annotation",
"metadata": {},
"outputs": [
{
@@ -382,7 +382,6 @@
},
{
"cell_type": "markdown",
- "id": "intermediate-color",
"metadata": {},
"source": [
"However, `wrapper()` has a reference to the original `say_whee()` as func, and calls that function between the two calls to `print()`."
@@ -390,7 +389,6 @@
},
{
"cell_type": "markdown",
- "id": "worst-dublin",
"metadata": {},
"source": [
"Put simply: **decorators wrap a function, modifying its behavior.**"
@@ -398,7 +396,6 @@
},
{
"cell_type": "markdown",
- "id": "designed-bouquet",
"metadata": {},
"source": [
"Before moving on, let’s have a look at a second example. Because `wrapper()` is a regular Python function, the way a decorator modifies a function can change dynamically. So as not to disturb your neighbors, the following example will only run the decorated code during the day:"
@@ -407,7 +404,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "grateful-genre",
"metadata": {},
"outputs": [],
"source": [
@@ -427,7 +423,6 @@
{
"cell_type": "code",
"execution_count": 75,
- "id": "bottom-storm",
"metadata": {},
"outputs": [],
"source": [
@@ -448,7 +443,6 @@
{
"cell_type": "code",
"execution_count": 76,
- "id": "australian-wonder",
"metadata": {},
"outputs": [],
"source": [
@@ -457,7 +451,6 @@
},
{
"cell_type": "markdown",
- "id": "unlikely-madrid",
"metadata": {},
"source": [
"If you try to call `say_whee()` after bedtime, nothing will happen:"
@@ -465,18 +458,16 @@
},
{
"cell_type": "markdown",
- "id": "ceramic-swing",
"metadata": {},
"source": [
- "\n",
- "## Syntactic Sugar!\n",
+ "\n",
+ "## Syntactic Sugar! [
](#table_of_contents)\n",
"\n",
"The way you decorated `say_whee()` above is a little clunky. First of all, you end up typing the name `say_whee` three times. In addition, the decoration gets a bit hidden away below the definition of the function."
]
},
{
"cell_type": "markdown",
- "id": "aquatic-washington",
"metadata": {},
"source": [
"Instead, Python allows you to use decorators in a simpler way with the `@` symbol, sometimes called the pie syntax (Barry Warsaw named this the 'pie-decorator' syntax, in honor of the Pie-thon Parrot shootout which occurred around the same time as the decorator syntax, and because the `@` looks a little like a pie.)\n",
@@ -487,7 +478,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "consistent-easter",
"metadata": {},
"outputs": [],
"source": [
@@ -510,7 +500,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "conceptual-questionnaire",
"metadata": {},
"outputs": [
{
@@ -529,7 +518,6 @@
},
{
"cell_type": "markdown",
- "id": "centered-supervisor",
"metadata": {},
"source": [
"So, `@my_decorator` is just an easier way of saying `say_whee = my_decorator(say_whee)`. It’s how you apply a decorator to a function."
@@ -537,11 +525,10 @@
},
{
"cell_type": "markdown",
- "id": "antique-border",
"metadata": {},
"source": [
"\n",
- "## Reusing Decorators\n",
+ "## Reusing Decorators [
](#table_of_contents)\n",
"\n",
"Recall that a decorator is just a regular Python function. All the usual tools for easy reusability are available. Let’s move the decorator to its own module that can be used in many other functions.\n",
"\n",
@@ -551,7 +538,6 @@
{
"cell_type": "code",
"execution_count": 47,
- "id": "curious-labor",
"metadata": {},
"outputs": [],
"source": [
@@ -564,7 +550,6 @@
},
{
"cell_type": "markdown",
- "id": "likely-there",
"metadata": {},
"source": [
"**Note:** You can name your inner function whatever you want, and a generic name like `wrapper()` is usually okay. You’ll see a lot of decorators in this article. To keep them apart, we’ll name the inner function with the same name as the decorator but with a wrapper_ prefix."
@@ -572,7 +557,6 @@
},
{
"cell_type": "markdown",
- "id": "increased-throw",
"metadata": {},
"source": [
"You can now use this new decorator in other files by doing a regular `import`:\n",
@@ -588,7 +572,6 @@
},
{
"cell_type": "markdown",
- "id": "minor-lesbian",
"metadata": {},
"source": [
"When you run this example, you should see that the original `say_whee()` is executed twice:"
@@ -597,7 +580,6 @@
{
"cell_type": "code",
"execution_count": 55,
- "id": "incoming-softball",
"metadata": {},
"outputs": [],
"source": [
@@ -609,7 +591,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "traditional-roman",
"metadata": {},
"outputs": [
{
@@ -627,16 +608,14 @@
},
{
"cell_type": "markdown",
- "id": "attempted-ethernet",
"metadata": {},
"source": [
"\n",
- "## Decorating Functions with Arguments"
+ "## Decorating Functions with Arguments [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "architectural-custody",
"metadata": {},
"source": [
"Say that you have a function that accepts some arguments. Can you still decorate it? Let’s try:"
@@ -645,7 +624,6 @@
{
"cell_type": "code",
"execution_count": 79,
- "id": "thrown-paintball",
"metadata": {},
"outputs": [],
"source": [
@@ -656,7 +634,6 @@
},
{
"cell_type": "markdown",
- "id": "induced-portuguese",
"metadata": {},
"source": [
"Unfortunately, running this code raises an error:"
@@ -665,7 +642,6 @@
{
"cell_type": "code",
"execution_count": 80,
- "id": "close-dealer",
"metadata": {},
"outputs": [
{
@@ -686,7 +662,6 @@
},
{
"cell_type": "markdown",
- "id": "catholic-disaster",
"metadata": {},
"source": [
"The problem is that the inner function `wrapper_do_twice()` does not take any arguments, but `name=\"World\"` was passed to it. You could fix this by letting `wrapper_do_twice()` accept one argument, but then it would not work for the `say_whee()` function you created earlier."
@@ -694,7 +669,6 @@
},
{
"cell_type": "markdown",
- "id": "related-atlanta",
"metadata": {},
"source": [
"The solution is to use `*args` and `**kwargs` in the inner wrapper function. Then it will accept an arbitrary number of positional and keyword arguments. Rewrite `decorators.py` as follows:"
@@ -702,8 +676,7 @@
},
{
"cell_type": "code",
- "execution_count": 93,
- "id": "neural-graphic",
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -720,7 +693,6 @@
},
{
"cell_type": "markdown",
- "id": "linear-toddler",
"metadata": {},
"source": [
"The `wrapper_do_twice()` inner function now accepts any number of arguments and passes them on to the function it decorates. Now both your `say_whee()` and `greet()` examples works:"
@@ -729,7 +701,6 @@
{
"cell_type": "code",
"execution_count": 96,
- "id": "subject-lunch",
"metadata": {},
"outputs": [
{
@@ -749,7 +720,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "subjective-parade",
"metadata": {},
"outputs": [
{
@@ -767,19 +737,17 @@
},
{
"cell_type": "markdown",
- "id": "injured-characterization",
"metadata": {},
"source": [
"\n",
- "## Returning Values From Decorated Functions\n",
+ "## Returning Values From Decorated Functions [
](#table_of_contents)\n",
"\n",
"What happens to the return value of decorated functions? Well, that’s up to the decorator to decide. Let’s say you decorate a simple function as follows:"
]
},
{
"cell_type": "code",
- "execution_count": 39,
- "id": "chubby-superior",
+ "execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
@@ -791,8 +759,7 @@
},
{
"cell_type": "code",
- "execution_count": 42,
- "id": "varying-decimal",
+ "execution_count": 11,
"metadata": {},
"outputs": [
{
@@ -801,7 +768,7 @@
"text": [
"Creating greeting\n",
"Creating greeting\n",
- "None\n"
+ "Hi Ali\n"
]
}
],
@@ -812,7 +779,6 @@
},
{
"cell_type": "markdown",
- "id": "institutional-brush",
"metadata": {},
"source": [
"Oops, your decorator ate the return value from the function."
@@ -820,7 +786,6 @@
},
{
"cell_type": "markdown",
- "id": "acceptable-template",
"metadata": {},
"source": [
"Because the `do_twice_wrapper()` doesn’t explicitly return a value, the call `return_greeting(\"Ali\")` ended up returning None."
@@ -828,7 +793,6 @@
},
{
"cell_type": "markdown",
- "id": "elementary-omega",
"metadata": {},
"source": [
"To fix this, you need to **make sure the wrapper function returns the return value of the decorated function**. Change your `decorators.py` file:"
@@ -837,7 +801,6 @@
{
"cell_type": "code",
"execution_count": 96,
- "id": "national-utilization",
"metadata": {},
"outputs": [],
"source": [
@@ -856,7 +819,6 @@
},
{
"cell_type": "markdown",
- "id": "occupied-optics",
"metadata": {},
"source": [
"The return value from the last execution of the function is returned:"
@@ -865,7 +827,6 @@
{
"cell_type": "code",
"execution_count": 98,
- "id": "sufficient-strengthening",
"metadata": {},
"outputs": [
{
@@ -883,11 +844,10 @@
},
{
"cell_type": "markdown",
- "id": "aquatic-latter",
"metadata": {},
"source": [
- "\n",
- "## Who Are You, Really?\n",
+ "\n",
+ "## Who Are You, Really? [
](#table_of_contents)\n",
"\n",
"A great convenience when working with Python, especially in the interactive shell, is its powerful introspection ability. Introspection is the ability of an object to know about its own attributes at runtime. For instance, a function knows its own name and documentation:"
]
@@ -895,7 +855,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "nasty-button",
"metadata": {},
"outputs": [],
"source": [
@@ -921,7 +880,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "talented-venezuela",
"metadata": {},
"outputs": [
{
@@ -942,7 +900,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "irish-johns",
"metadata": {},
"outputs": [
{
@@ -963,7 +920,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "criminal-consolidation",
"metadata": {},
"outputs": [
{
@@ -982,7 +938,6 @@
},
{
"cell_type": "markdown",
- "id": "conceptual-messaging",
"metadata": {},
"source": [
"The introspection works for functions you define yourself as well:"
@@ -991,7 +946,6 @@
{
"cell_type": "code",
"execution_count": 49,
- "id": "private-novelty",
"metadata": {},
"outputs": [
{
@@ -1012,7 +966,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "diagnostic-promise",
"metadata": {},
"outputs": [
{
@@ -1033,7 +986,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "continent-actress",
"metadata": {},
"outputs": [
{
@@ -1053,7 +1005,6 @@
},
{
"cell_type": "markdown",
- "id": "spatial-spank",
"metadata": {},
"source": [
"However, after being decorated, `say_whee()` has gotten very confused about its identity. It now reports being the `wrapper_do_twice()` inner function inside the `do_twice()` decorator. Although technically true, this is not very useful information."
@@ -1061,7 +1012,6 @@
},
{
"cell_type": "markdown",
- "id": "still-continuity",
"metadata": {},
"source": [
"To fix this, decorators should use the `@functools.wraps` decorator, which will preserve information about the original function. Update `decorators.py` again:"
@@ -1070,7 +1020,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "asian-matthew",
"metadata": {},
"outputs": [],
"source": [
@@ -1093,7 +1042,6 @@
},
{
"cell_type": "markdown",
- "id": "fixed-cleaners",
"metadata": {},
"source": [
"You do not need to change anything about the decorated `say_whee()` function:"
@@ -1102,7 +1050,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "electoral-viewer",
"metadata": {},
"outputs": [
{
@@ -1123,7 +1070,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "oriented-quick",
"metadata": {},
"outputs": [
{
@@ -1144,7 +1090,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "effective-cursor",
"metadata": {},
"outputs": [
{
@@ -1165,7 +1110,6 @@
},
{
"cell_type": "markdown",
- "id": "raising-timing",
"metadata": {},
"source": [
"Much better! Now `say_whee()` is still itself after decoration."
@@ -1173,11 +1117,10 @@
},
{
"cell_type": "markdown",
- "id": "classical-price",
"metadata": {},
"source": [
- "\n",
- "## A Few Real World Examples\n",
+ "\n",
+ "## A Few Real World Examples [
](#table_of_contents)\n",
"\n",
"Let’s look at a few more useful examples of decorators. You’ll notice that they’ll mainly follow the same pattern that you’ve learned so far:"
]
@@ -1185,7 +1128,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "after-gather",
"metadata": {},
"outputs": [],
"source": [
@@ -1198,12 +1140,12 @@
" value = func(*args, **kwargs)\n",
" # Do something after\n",
" return value\n",
+ "\n",
" return wrapper_decorator"
]
},
{
"cell_type": "markdown",
- "id": "aware-expansion",
"metadata": {},
"source": [
"This formula is a good boilerplate template for building more complex decorators."
@@ -1211,17 +1153,16 @@
},
{
"cell_type": "markdown",
- "id": "valuable-yacht",
"metadata": {},
"source": [
- "### Timing Functions\n",
+ "\n",
+ "### Timing Functions [
](#table_of_contents)\n",
"iLet’s start by creating a `@timer` decorator. It will measure the time a function takes to execute and print the duration to the console. Here’s the code:"
]
},
{
"cell_type": "code",
"execution_count": 143,
- "id": "perfect-belle",
"metadata": {},
"outputs": [],
"source": [
@@ -1250,7 +1191,6 @@
{
"cell_type": "code",
"execution_count": 144,
- "id": "separated-courage",
"metadata": {},
"outputs": [
{
@@ -1267,7 +1207,6 @@
},
{
"cell_type": "markdown",
- "id": "substantial-sierra",
"metadata": {},
"source": [
"This decorator works by storing the time just before the function starts running (at the line marked # 1) and just after the function finishes (at # 2). The time the function takes is then the difference between the two (at # 3). We use the `time.perf_counter()` function, which does a good job of measuring time intervals. Here are some examples of timings:"
@@ -1275,7 +1214,6 @@
},
{
"cell_type": "markdown",
- "id": "hindu-recorder",
"metadata": {},
"source": [
"**Note:** The `@timer` decorator is great if you just want to get an idea about the runtime of your functions. If you want to do more precise measurements of code, you should instead consider the `timeit` module in the standard library. It temporarily disables garbage collection and runs multiple trials to strip out noise from quick function calls."
@@ -1283,17 +1221,16 @@
},
{
"cell_type": "markdown",
- "id": "fixed-region",
"metadata": {},
"source": [
- "### Debugging Code\n",
+ "\n",
+ "### Debugging Code [
](#table_of_contents)\n",
"The following `@debug` decorator will print the arguments a function is called with as well as its return value every time the function is called:"
]
},
{
"cell_type": "code",
"execution_count": 1,
- "id": "removable-chassis",
"metadata": {},
"outputs": [],
"source": [
@@ -1315,7 +1252,6 @@
},
{
"cell_type": "markdown",
- "id": "native-button",
"metadata": {},
"source": [
"The signature is created by joining the string representations of all the arguments. The numbers in the following list correspond to the numbered comments in the code:\n",
@@ -1328,7 +1264,6 @@
},
{
"cell_type": "markdown",
- "id": "informal-world",
"metadata": {},
"source": [
"Let’s see how the decorator works in practice by applying it to a simple function with one position and one keyword argument:"
@@ -1337,7 +1272,6 @@
{
"cell_type": "code",
"execution_count": 189,
- "id": "lesbian-intelligence",
"metadata": {},
"outputs": [],
"source": [
@@ -1351,7 +1285,6 @@
},
{
"cell_type": "markdown",
- "id": "alert-dress",
"metadata": {},
"source": [
"Note how the `@debug` decorator prints the signature and return value of the `make_greeting()` function:"
@@ -1360,7 +1293,6 @@
{
"cell_type": "code",
"execution_count": 190,
- "id": "written-costume",
"metadata": {},
"outputs": [
{
@@ -1389,7 +1321,6 @@
{
"cell_type": "code",
"execution_count": 191,
- "id": "suitable-instrumentation",
"metadata": {},
"outputs": [
{
@@ -1417,7 +1348,6 @@
},
{
"cell_type": "markdown",
- "id": "seasonal-union",
"metadata": {},
"source": [
"This example might not seem immediately useful since the `@debug` decorator just repeats what you just wrote. It’s more powerful when applied to small convenience functions that you don’t call directly yourself.\n",
@@ -1428,7 +1358,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "opposed-romantic",
"metadata": {},
"outputs": [],
"source": [
@@ -1443,17 +1372,15 @@
},
{
"cell_type": "markdown",
- "id": "tribal-cassette",
"metadata": {},
"source": [
"This example also shows how you can apply a decorator to a function that has already been defined. The approximation of e is based on the following series expansion:\n",
"\n",
- "$e = \\sum_{n=0}^{\\infty} \\frac{1}{n} = \\frac{1}{0!} + \\frac{1}{1!} + \\frac{1}{2!} + \\frac{1}{3!} + ...$"
+ "$e = \\sum_{n=0}^{\\infty} \\frac{1}{n!} = \\frac{1}{0!} + \\frac{1}{1!} + \\frac{1}{2!} + \\frac{1}{3!} + ...$"
]
},
{
"cell_type": "markdown",
- "id": "optimum-causing",
"metadata": {},
"source": [
"When calling the `approximate_e()` function, you can see the `@debug` decorator at work:"
@@ -1462,7 +1389,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "happy-bidder",
"metadata": {},
"outputs": [
{
@@ -1498,10 +1424,10 @@
},
{
"cell_type": "markdown",
- "id": "corresponding-cabinet",
"metadata": {},
"source": [
- "### Slowing Down Code\n",
+ "\n",
+ "### Slowing Down Code [
](#table_of_contents)\n",
"\n",
"iThis next example might not seem very useful. Why would you want to slow down your Python code? Probably the most common use case is that you want to rate-limit a function that continuously checks whether a resource—like a web page—has changed. The `@slow_down` decorator will sleep one second before it calls the decorated function:"
]
@@ -1509,7 +1435,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "brave-logic",
"metadata": {},
"outputs": [],
"source": [
@@ -1536,7 +1461,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "durable-austria",
"metadata": {},
"outputs": [
{
@@ -1571,7 +1495,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/10.2 Fancy Decorators.ipynb b/Python/04. Advanced/03.2 Fancy Decorators.ipynb
similarity index 88%
rename from python/04. Advanced/10.2 Fancy Decorators.ipynb
rename to Python/04. Advanced/03.2 Fancy Decorators.ipynb
index 925702e..f981cb6 100755
--- a/python/04. Advanced/10.2 Fancy Decorators.ipynb
+++ b/Python/04. Advanced/03.2 Fancy Decorators.ipynb
@@ -2,27 +2,50 @@
"cells": [
{
"cell_type": "markdown",
- "id": "forced-sierra",
"metadata": {},
"source": [
- "# Fancy Decorators\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "tags": []
+ },
+ "source": [
+ "#
Fancy Decorators \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
"\n",
- "In the second part of this tutorial, we’ll explore more advanced features, including how to use the following:\n",
- " \n",
- "* [1. Several decorators on one function](#several_decorators_on_one_function)\n",
- "* [2. Decorators with arguments](#decorators_with_arguments)\n",
- "* [3. Decorators that can optionally take arguments](#decorators_with_optional_arguments)\n",
- "* [4. Stateful decorators](#stateful_decorators)\n",
- "* [5. Classes as decorators](#classes_as_decorators)"
+ "\n",
+ "* [Several decorators on one function](#several_decorators_on_one_function)\n",
+ "* [Decorators With Arguments](#decorators_with_arguments)\n",
+ "* [Decorators that Can Optionally Take Arguments](#decorators_that_can_optionally_take_arguments)\n",
+ "* [Stateful Decorators](#stateful_decorators)\n",
+ "* [Classes as Decorators](#classes_as_decorators)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the second part of this tutorial, we’ll explore more advanced features, including how to use the following:"
]
},
{
"cell_type": "markdown",
- "id": "funky-whole",
"metadata": {},
"source": [
"\n",
- "## Several decorators on one function\n",
+ "## Several decorators on one function [
](#table_of_contents)\n",
"\n",
"You can apply several decorators to a function by stacking them on top of each other:"
]
@@ -30,7 +53,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "solid-wholesale",
"metadata": {},
"outputs": [],
"source": [
@@ -59,8 +81,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "id": "overhead-bennett",
+ "execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
@@ -72,7 +93,6 @@
},
{
"cell_type": "markdown",
- "id": "endangered-thinking",
"metadata": {},
"source": [
"Think about this as the decorators being executed in the order they are listed. In other words, `@debug` calls `@do_twice`, which calls `greet()`, or `debug(do_twice(greet()))`:"
@@ -80,10 +100,10 @@
},
{
"cell_type": "code",
- "execution_count": 3,
- "id": "smooth-preliminary",
+ "execution_count": 13,
"metadata": {
- "scrolled": true
+ "scrolled": true,
+ "tags": []
},
"outputs": [
{
@@ -105,7 +125,6 @@
},
{
"cell_type": "markdown",
- "id": "united-oakland",
"metadata": {},
"source": [
"Observe the difference if we change the order of `@debug` and `@do_twice`:"
@@ -113,8 +132,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
- "id": "catholic-concentrate",
+ "execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
@@ -126,7 +144,6 @@
},
{
"cell_type": "markdown",
- "id": "hungarian-removal",
"metadata": {},
"source": [
"In this case, `@do_twice` will be applied to `@debug` as well:"
@@ -135,7 +152,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "excited-support",
"metadata": {},
"outputs": [
{
@@ -155,16 +171,14 @@
},
{
"cell_type": "markdown",
- "id": "vulnerable-privilege",
"metadata": {},
"source": [
"\n",
- "## Decorators With Arguments"
+ "## Decorators With Arguments [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "attached-northern",
"metadata": {},
"source": [
"Sometimes, it’s useful to pass arguments to your decorators. For instance, `@do_twice` could be extended to a `@repeat(num_times)` decorator. The number of times to execute the decorated function could then be given as an argument.\n",
@@ -186,7 +200,6 @@
},
{
"cell_type": "markdown",
- "id": "medical-satisfaction",
"metadata": {},
"source": [
"So far, the name written after the `@` has referred to a function object that can be called with another function. To be consistent, you then need `repeat(num_times=4)` to return a function object that can act as a decorator. Luckily, you already know how to return functions! In general, you want something like the following:"
@@ -195,7 +208,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "warming-extreme",
"metadata": {},
"outputs": [],
"source": [
@@ -207,7 +219,6 @@
},
{
"cell_type": "markdown",
- "id": "broadband-helicopter",
"metadata": {},
"source": [
"Typically, the decorator creates and returns an inner wrapper function, so writing the example out in full will give you an inner function within an inner function. While this might sound like the programming equivalent of the Inception movie, we’ll untangle it all in a moment:"
@@ -216,7 +227,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "authorized-programming",
"metadata": {},
"outputs": [],
"source": [
@@ -233,7 +243,6 @@
},
{
"cell_type": "markdown",
- "id": "dried-missile",
"metadata": {},
"source": [
"It looks a little messy, but we have only put the same decorator pattern you have seen many times by now inside one additional def that handles the arguments to the decorator. Let’s start with the innermost function:\n",
@@ -248,7 +257,6 @@
},
{
"cell_type": "markdown",
- "id": "tired-platform",
"metadata": {},
"source": [
"This `wrapper_repeat()` function takes arbitrary arguments and returns the value of the decorated function, `func()`. This wrapper function also contains the loop that calls the decorated function num_times times. This is no different from the earlier wrapper functions you have seen, except that it is using the `num_times` parameter that must be supplied from the outside."
@@ -256,7 +264,6 @@
},
{
"cell_type": "markdown",
- "id": "aggressive-fifty",
"metadata": {},
"source": [
"One step out, you’ll find the decorator function:\n",
@@ -271,7 +278,6 @@
},
{
"cell_type": "markdown",
- "id": "paperback-modeling",
"metadata": {},
"source": [
"Again, `decorator_repeat()` looks exactly like the decorator functions you have written earlier, except that it’s named differently. That’s because we reserve the base name—`repeat()`—for the outermost function, which is the one the user will call.\n",
@@ -288,7 +294,6 @@
},
{
"cell_type": "markdown",
- "id": "handmade-hopkins",
"metadata": {},
"source": [
"There are a few subtle things happening in the repeat() function:\n",
@@ -299,7 +304,6 @@
},
{
"cell_type": "markdown",
- "id": "martial-wilson",
"metadata": {},
"source": [
"With everything set up, let’s see if the results are as expected:"
@@ -308,7 +312,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "occupied-gazette",
"metadata": {},
"outputs": [],
"source": [
@@ -325,7 +328,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "powerful-kentucky",
"metadata": {},
"outputs": [
{
@@ -345,18 +347,16 @@
},
{
"cell_type": "markdown",
- "id": "resident-florist",
"metadata": {},
"source": [
- "\n",
- "## Decorators that Can Optionally Take Arguments\n",
+ "\n",
+ "## Decorators that Can Optionally Take Arguments [
](#table_of_contents)\n",
"\n",
"With a little bit of care, you can also define decorators that can be used both with and without arguments. Most likely, you don’t need this, but it is nice to have the flexibility."
]
},
{
"cell_type": "markdown",
- "id": "whole-activity",
"metadata": {},
"source": [
"As you saw in the previous section, when a decorator uses arguments, you need to add an extra outer function. The challenge is for your code to figure out if the decorator has been called with or without arguments."
@@ -364,7 +364,6 @@
},
{
"cell_type": "markdown",
- "id": "posted-threat",
"metadata": {},
"source": [
"Since the function to decorate is only passed in directly if the decorator is called without arguments, the function must be an optional argument. This means that the decorator arguments must all be specified by keyword. You can enforce this with the special `*` syntax, which means that all following parameters are keyword-only:\n",
@@ -383,7 +382,6 @@
},
{
"cell_type": "markdown",
- "id": "saved-subsection",
"metadata": {},
"source": [
"Here, the `_func` argument acts as a marker, noting whether the decorator has been called with arguments or not:\n",
@@ -398,7 +396,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "interpreted-finnish",
"metadata": {},
"outputs": [],
"source": [
@@ -419,7 +416,6 @@
},
{
"cell_type": "markdown",
- "id": "naked-prediction",
"metadata": {},
"source": [
"Compare this with the original `@repeat`. The only changes are the added `_func` parameter and the `if-else` at the end."
@@ -427,7 +423,6 @@
},
{
"cell_type": "markdown",
- "id": "weighted-jefferson",
"metadata": {},
"source": [
"These examples show that `@repeat` can now be used with or without arguments:"
@@ -436,7 +431,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "mature-apollo",
"metadata": {},
"outputs": [],
"source": [
@@ -459,7 +453,6 @@
},
{
"cell_type": "markdown",
- "id": "stone-crisis",
"metadata": {},
"source": [
"Recall that the default value of `num_times` is `2`:"
@@ -468,7 +461,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "stylish-litigation",
"metadata": {},
"outputs": [
{
@@ -487,7 +479,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "together-keeping",
"metadata": {},
"outputs": [
{
@@ -506,16 +497,14 @@
},
{
"cell_type": "markdown",
- "id": "productive-license",
"metadata": {},
"source": [
"\n",
- "## Stateful Decorators"
+ "## Stateful Decorators [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "thermal-october",
"metadata": {},
"source": [
"Sometimes, it’s useful to have **a decorator that can keep track of state**. As a simple example, we will create a decorator that counts the number of times a function is called."
@@ -523,7 +512,6 @@
},
{
"cell_type": "markdown",
- "id": "arabic-cancellation",
"metadata": {},
"source": [
"In the next section (_Classes as Decorators_), you will see how to use classes to keep state. But in simple cases, you can also get away with using function attributes:"
@@ -532,7 +520,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "juvenile-mexico",
"metadata": {},
"outputs": [],
"source": [
@@ -558,7 +545,6 @@
},
{
"cell_type": "markdown",
- "id": "peaceful-preview",
"metadata": {},
"source": [
"The state—the number of calls to the function—is stored in the function attribute `.num_calls` on the wrapper function. Here is the effect of using it:"
@@ -567,7 +553,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "arbitrary-arrangement",
"metadata": {},
"outputs": [
{
@@ -586,7 +571,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "allied-prototype",
"metadata": {},
"outputs": [
{
@@ -605,7 +589,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "included-telephone",
"metadata": {},
"outputs": [
{
@@ -625,16 +608,14 @@
},
{
"cell_type": "markdown",
- "id": "fixed-breakdown",
"metadata": {},
"source": [
"\n",
- "## Classes as Decorators"
+ "## Classes as Decorators [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "ordered-replacement",
"metadata": {},
"source": [
"The typical way to maintain state is by using classes. In this section, you’ll see how to rewrite the `@count_calls` example from the previous section **using a class as a decorator**."
@@ -642,7 +623,6 @@
},
{
"cell_type": "markdown",
- "id": "experienced-parallel",
"metadata": {},
"source": [
"Recall that the decorator syntax `@my_decorator` is just an easier way of saying `func = my_decorator(func)`. Therefore, if `my_decorator` is a class, it needs to take func as an argument in its `.__init__()` method. Furthermore, the class instance needs to be callable so that it can stand in for the decorated function."
@@ -650,7 +630,6 @@
},
{
"cell_type": "markdown",
- "id": "sexual-ecuador",
"metadata": {},
"source": [
"For a class instance to be callable, you implement the special `.__call__()` method:"
@@ -659,7 +638,6 @@
{
"cell_type": "code",
"execution_count": 78,
- "id": "ideal-football",
"metadata": {},
"outputs": [],
"source": [
@@ -674,7 +652,6 @@
},
{
"cell_type": "markdown",
- "id": "smoking-chain",
"metadata": {},
"source": [
"The `.__call__()` method is executed each time you try to call an instance of the class:"
@@ -683,7 +660,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "thick-preparation",
"metadata": {},
"outputs": [],
"source": [
@@ -693,7 +669,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "expired-shift",
"metadata": {},
"outputs": [
{
@@ -711,7 +686,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "noted-boulder",
"metadata": {},
"outputs": [
{
@@ -728,7 +702,6 @@
},
{
"cell_type": "markdown",
- "id": "structural-racing",
"metadata": {},
"source": [
"Therefore, a typical implementation of a decorator class needs to implement `.__init__()` and `.__call__()`:"
@@ -737,13 +710,11 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "entertaining-secondary",
"metadata": {},
"outputs": [],
"source": [
"import functools\n",
"\n",
- "\n",
"class CountCalls:\n",
" def __init__(self, func, *, num_times=2):\n",
" functools.update_wrapper(self, func)\n",
@@ -766,7 +737,6 @@
},
{
"cell_type": "markdown",
- "id": "assured-cowboy",
"metadata": {},
"source": [
"The `.__init__()` method must store a reference to the function and can do any other necessary initialization. The `.__call__()` method will be called instead of the decorated function. It does essentially the same thing as the `wrapper() `function in our earlier examples. Note that you need to use the `functools.update_wrapper()` function instead of `@functools.wraps`."
@@ -774,7 +744,6 @@
},
{
"cell_type": "markdown",
- "id": "mature-incidence",
"metadata": {},
"source": [
"This `@CountCalls` decorator works the same as the one in the previous section:"
@@ -783,7 +752,6 @@
{
"cell_type": "code",
"execution_count": 54,
- "id": "artistic-comparison",
"metadata": {},
"outputs": [
{
@@ -802,7 +770,6 @@
{
"cell_type": "code",
"execution_count": 55,
- "id": "bibliographic-wednesday",
"metadata": {},
"outputs": [
{
@@ -821,7 +788,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "international-momentum",
"metadata": {},
"outputs": [
{
@@ -840,7 +806,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "external-rolling",
"metadata": {},
"outputs": [
{
@@ -875,7 +840,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/10.3 Decorating Classes.ipynb b/Python/04. Advanced/03.3 Decorating Classes.ipynb
similarity index 89%
rename from python/04. Advanced/10.3 Decorating Classes.ipynb
rename to Python/04. Advanced/03.3 Decorating Classes.ipynb
index 60bddfe..fa65f3d 100755
--- a/python/04. Advanced/10.3 Decorating Classes.ipynb
+++ b/Python/04. Advanced/03.3 Decorating Classes.ipynb
@@ -2,28 +2,55 @@
"cells": [
{
"cell_type": "markdown",
- "id": "located-belgium",
"metadata": {},
"source": [
- "# Decorating Classes\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Decorating Classes \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
"\n",
- "There are two different ways you can use decorators on classes.\n",
"\n",
- "The first one is very close to what you have already done with functions: you can decorate the methods of a class. This was one of the motivations for introducing decorators back in the day."
+ "* [Decorating the Whole Class](#decorating_the_whole_class)\n",
+ " * [Creating Singletons](#creating_singletons)\n",
+ "* [OOP: Instance Method, Static Method, Class Method](#oop:_instance_method,_static_method,_class_method)\n",
+ " * [Instance Method](#instance_method)\n",
+ " * [Class Method](#class_method)\n",
+ " * [Static Methods](#static_methods)\n",
+ " * [Let's See Them in Action](#let's_see_them_in_action)\n",
+ " * [Example](#example)\n",
+ " * [Delicious Pizza Factories With `@classmethod`](#delicious_pizza_factories_with_`@classmethod`)\n",
+ " * [When To Use Static Methods](#when_to_use_static_methods)\n",
+ " * [Key Takeaways](#key_takeaways)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "incident-yemen",
"metadata": {},
"source": [
+ "There are two different ways you can use decorators on classes.\n",
+ "\n",
+ "The first one is very close to what you have already done with functions: you can decorate the methods of a class. This was one of the motivations for introducing decorators back in the day.\n",
+ "\n",
"Let’s define a class where we decorate some of its methods using the `@debug` and `@timer` decorators from earlier:"
]
},
{
"cell_type": "code",
"execution_count": 1,
- "id": "blind-cradle",
"metadata": {},
"outputs": [],
"source": [
@@ -46,7 +73,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "mechanical-district",
"metadata": {},
"outputs": [],
"source": [
@@ -69,7 +95,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "legendary-microphone",
"metadata": {},
"outputs": [],
"source": [
@@ -87,7 +112,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "crazy-check",
"metadata": {},
"outputs": [
{
@@ -106,7 +130,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "italian-sherman",
"metadata": {},
"outputs": [
{
@@ -123,15 +146,14 @@
},
{
"cell_type": "markdown",
- "id": "precise-arrangement",
"metadata": {},
"source": [
- "## Decorating the Whole Class"
+ "\n",
+ "## Decorating the Whole Class [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "future-special",
"metadata": {},
"source": [
"The other way to use decorators on classes is to decorate the whole class.\n",
@@ -152,7 +174,6 @@
},
{
"cell_type": "markdown",
- "id": "cellular-disposal",
"metadata": {},
"source": [
"The meaning of the syntax is similar to the function decorators. In the example above, you could have done the decoration by writing `PlayingCard = dataclass(PlayingCard)`."
@@ -160,7 +181,6 @@
},
{
"cell_type": "markdown",
- "id": "cooked-spoke",
"metadata": {},
"source": [
"Writing a class decorator is very similar to writing a function decorator. The only difference is that the decorator will receive a class and not a function as an argument. In fact, all the decorators you saw already will work as class decorators. When you are using them on a class instead of a function, their effect might not be what you want. In the following example, the `@timer` decorator is applied to a class:"
@@ -169,7 +189,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "noticed-litigation",
"metadata": {},
"outputs": [],
"source": [
@@ -202,7 +221,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "romantic-values",
"metadata": {},
"outputs": [
{
@@ -220,18 +238,16 @@
},
{
"cell_type": "markdown",
- "id": "legitimate-survival",
"metadata": {},
"source": [
"Decorating a class does not decorate its methods. Recall that `@timer` is just shorthand for `TimeWaster = timer(TimeWaster)`.\n",
"\n",
- "`-->` Here, `@timer` only measures the time it takes to instantiate the class:"
+ "Here, `@timer` only measures the time it takes to instantiate the class:"
]
},
{
"cell_type": "code",
"execution_count": 7,
- "id": "plastic-breath",
"metadata": {},
"outputs": [
{
@@ -249,7 +265,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "awful-unknown",
"metadata": {},
"outputs": [
{
@@ -266,7 +281,6 @@
},
{
"cell_type": "markdown",
- "id": "pressing-blogger",
"metadata": {},
"source": [
"An example defining a proper class decorator is `@singleton` which ensures that there is only one instance of a class."
@@ -274,15 +288,14 @@
},
{
"cell_type": "markdown",
- "id": "absolute-tampa",
"metadata": {},
"source": [
- "### Creating Singletons"
+ "\n",
+ "### Creating Singletons [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "elementary-wholesale",
"metadata": {},
"source": [
"A singleton is a class with only one instance. There are several singletons in Python that you use frequently, including `None`, `True`, and `False`. It is the fact that `None` is a singleton that allows you to compare for `None` using the `is` keyword."
@@ -290,7 +303,6 @@
},
{
"cell_type": "markdown",
- "id": "threatened-cartoon",
"metadata": {},
"source": [
"Using `is` returns `True` only for objects that are the exact same instance. The following `@singleton` decorator turns a class into a singleton by storing the first instance of the class as an attribute. Later attempts at creating an instance simply return the stored instance:"
@@ -299,7 +311,6 @@
{
"cell_type": "code",
"execution_count": 54,
- "id": "impressive-battery",
"metadata": {},
"outputs": [],
"source": [
@@ -324,7 +335,6 @@
},
{
"cell_type": "markdown",
- "id": "alive-position",
"metadata": {},
"source": [
"As you see, this class decorator follows the same template as our function decorators. The only difference is that we are using `cls` instead of `func` as the parameter name to indicate that it is meant to be a class decorator."
@@ -332,7 +342,6 @@
},
{
"cell_type": "markdown",
- "id": "painted-triumph",
"metadata": {},
"source": [
"Let’s see if it works:"
@@ -341,7 +350,6 @@
{
"cell_type": "code",
"execution_count": 55,
- "id": "possible-hindu",
"metadata": {},
"outputs": [
{
@@ -359,7 +367,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "available-victor",
"metadata": {},
"outputs": [],
"source": [
@@ -369,7 +376,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "opened-associate",
"metadata": {},
"outputs": [
{
@@ -390,7 +396,6 @@
{
"cell_type": "code",
"execution_count": 58,
- "id": "ambient-gospel",
"metadata": {},
"outputs": [
{
@@ -411,7 +416,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "based-jefferson",
"metadata": {},
"outputs": [
{
@@ -431,7 +435,6 @@
},
{
"cell_type": "markdown",
- "id": "critical-neighbor",
"metadata": {},
"source": [
"It seems clear that first_one is indeed the exact same instance as another_one."
@@ -439,15 +442,14 @@
},
{
"cell_type": "markdown",
- "id": "bizarre-simpson",
"metadata": {},
"source": [
- "## OOP: Instance Method, Static Method, Class Method"
+ "\n",
+ "## OOP: Instance Method, Static Method, Class Method [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "inside-metallic",
"metadata": {},
"source": [
"Some commonly used decorators that are even built-ins in Python are `@classmethod` and `@staticmethod`. The `@classmethod` and `@staticmethod` decorators are used to define methods inside a class namespace that are not connected to a particular instance of that class"
@@ -456,7 +458,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "annoying-recall",
"metadata": {},
"outputs": [],
"source": [
@@ -476,7 +477,6 @@
{
"cell_type": "code",
"execution_count": 128,
- "id": "expensive-bubble",
"metadata": {},
"outputs": [],
"source": [
@@ -500,7 +500,6 @@
{
"cell_type": "code",
"execution_count": 82,
- "id": "other-thriller",
"metadata": {},
"outputs": [
{
@@ -521,7 +520,6 @@
{
"cell_type": "code",
"execution_count": 127,
- "id": "chief-positive",
"metadata": {},
"outputs": [
{
@@ -542,7 +540,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "welcome-garden",
"metadata": {},
"outputs": [],
"source": [
@@ -551,17 +548,16 @@
},
{
"cell_type": "markdown",
- "id": "weighted-fortune",
"metadata": {},
"source": [
- "### Instance Method\n",
+ "\n",
+ "### Instance Method [
](#table_of_contents)\n",
"\n",
"The first method on `MyClass`, called `method`, is a regular instance method. That’s the basic, no-frills method type you’ll use most of the time. You can see the method takes one parameter, `self`, which points to an instance of `MyClass` when the method is called (but of course instance methods can accept more than just one parameter)."
]
},
{
"cell_type": "markdown",
- "id": "alternate-beverage",
"metadata": {},
"source": [
"Through the `self` parameter, instance methods can freely access attributes and other methods on the same object. This gives them a lot of power when it comes to modifying an object’s state."
@@ -569,7 +565,6 @@
},
{
"cell_type": "markdown",
- "id": "instructional-handbook",
"metadata": {},
"source": [
"Not only can they modify object state, instance methods can also access the class itself through the `self.__class__` attribute. This means instance methods can also modify class state."
@@ -577,10 +572,10 @@
},
{
"cell_type": "markdown",
- "id": "flexible-combine",
"metadata": {},
"source": [
- "### Class Method\n",
+ "\n",
+ "### Class Method [
](#table_of_contents)\n",
"\n",
"Let’s compare that to the second method, `MyClass.classmethod`. I marked this method with a `@classmethod` decorator to flag it as a class method.\n",
"\n",
@@ -591,10 +586,10 @@
},
{
"cell_type": "markdown",
- "id": "american-marina",
"metadata": {},
"source": [
- "### Static Methods\n",
+ "\n",
+ "### Static Methods [
](#table_of_contents)\n",
"\n",
"The third method, `MyClass.staticmethod` was marked with a `@staticmethod` decorator to flag it as a static method.\n",
"\n",
@@ -605,15 +600,14 @@
},
{
"cell_type": "markdown",
- "id": "contained-pension",
"metadata": {},
"source": [
- "### Let's See Them in Action"
+ "\n",
+ "### Let's See Them in Action [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "aggregate-suspension",
"metadata": {},
"source": [
"Here’s what happens when we call an **instance method**:"
@@ -622,7 +616,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "periodic-leadership",
"metadata": {},
"outputs": [
{
@@ -643,7 +636,6 @@
},
{
"cell_type": "markdown",
- "id": "gross-candle",
"metadata": {},
"source": [
"This confirmed that `method` (the instance method) has access to the object instance (printed as ``) via the `self` argument."
@@ -651,7 +643,6 @@
},
{
"cell_type": "markdown",
- "id": "mediterranean-chile",
"metadata": {},
"source": [
"When the method is called, Python replaces the `self` argument with the instance object, `obj`. We could ignore the syntactic sugar of the dot-call syntax (`obj.method()`) and pass the instance object manually to get the same result:"
@@ -660,7 +651,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "owned-organizer",
"metadata": {},
"outputs": [
{
@@ -680,7 +670,6 @@
},
{
"cell_type": "markdown",
- "id": "balanced-piano",
"metadata": {},
"source": [
"By the way, instance methods can also access the class itself through the `self.__class__` attribute. This makes instance methods powerful in terms of access restrictions - they can modify state on the object instance and on the class itself."
@@ -688,7 +677,6 @@
},
{
"cell_type": "markdown",
- "id": "operational-bangkok",
"metadata": {},
"source": [
"Let’s try out the **class method** next:"
@@ -697,7 +685,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "european-accreditation",
"metadata": {},
"outputs": [
{
@@ -717,7 +704,6 @@
},
{
"cell_type": "markdown",
- "id": "organizational-appendix",
"metadata": {},
"source": [
"Calling `classmethod()` showed us it doesn’t have access to the `` object, but only to the `` object, representing the class itself (everything in Python is an object, even classes themselves)."
@@ -725,7 +711,6 @@
},
{
"cell_type": "markdown",
- "id": "catholic-dressing",
"metadata": {},
"source": [
"Notice how Python automatically passes the class as the first argument to the function when we call `MyClass.classmethod()`. Calling a method in Python through the dot syntax triggers this behavior. The `self` parameter on instance methods works the same way."
@@ -733,7 +718,6 @@
},
{
"cell_type": "markdown",
- "id": "bright-tobago",
"metadata": {},
"source": [
"Please note that naming these parameters `self` and `cls` is just a convention. You could just as easily name them `the_object` and `the_class` and get the same result. All that matters is that they’re positioned first in the parameter list for the method."
@@ -741,7 +725,6 @@
},
{
"cell_type": "markdown",
- "id": "considered-chicago",
"metadata": {},
"source": [
"Time to call the **static method** now:"
@@ -750,7 +733,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "monthly-disclosure",
"metadata": {},
"outputs": [
{
@@ -770,7 +752,6 @@
},
{
"cell_type": "markdown",
- "id": "respective-butler",
"metadata": {},
"source": [
"Behind the scenes Python simply enforces the access restrictions by not passing in the `self` or the `cls` argument when a static method gets called using the dot syntax."
@@ -778,7 +759,6 @@
},
{
"cell_type": "markdown",
- "id": "personalized-boutique",
"metadata": {},
"source": [
"This confirms that static methods can neither access the object instance state nor the class state. They work like regular functions but belong to the class’s (and every instance’s) namespace."
@@ -786,7 +766,6 @@
},
{
"cell_type": "markdown",
- "id": "certified-capture",
"metadata": {},
"source": [
"Now, let’s take a look at what happens when we attempt to call these methods on the class itself - without creating an object instance beforehand:"
@@ -795,7 +774,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "desirable-tomato",
"metadata": {},
"outputs": [
{
@@ -816,7 +794,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "residential-luther",
"metadata": {},
"outputs": [
{
@@ -837,7 +814,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "surrounded-providence",
"metadata": {},
"outputs": [
{
@@ -858,7 +834,6 @@
},
{
"cell_type": "markdown",
- "id": "satisfactory-accident",
"metadata": {},
"source": [
"We were able to call `classmethod()` and `staticmethod()` just fine, but attempting to call the instance method `method()` failed with a `TypeError`."
@@ -866,7 +841,6 @@
},
{
"cell_type": "markdown",
- "id": "superb-force",
"metadata": {},
"source": [
"And this is to be expected — this time we didn’t create an object instance and tried calling an instance function directly on the class blueprint itself. This means there is no way for Python to populate the self argument and therefore the call fails."
@@ -874,15 +848,14 @@
},
{
"cell_type": "markdown",
- "id": "public-litigation",
"metadata": {},
"source": [
- "### Example"
+ "\n",
+ "### Example [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "executive-suite",
"metadata": {},
"source": [
"An example using this bare-bones Pizza class:"
@@ -891,7 +864,6 @@
{
"cell_type": "code",
"execution_count": 95,
- "id": "blocked-delay",
"metadata": {},
"outputs": [],
"source": [
@@ -905,10 +877,10 @@
},
{
"cell_type": "markdown",
- "id": "surprising-dining",
"metadata": {},
"source": [
- "#### Delicious Pizza Factories With `@classmethod`\n",
+ "\n",
+ "#### Delicious Pizza Factories With `@classmethod` [
](#table_of_contents)\n",
"\n",
"If you’ve had any exposure to pizza in the real world you’ll know that there are many delicious variations available:"
]
@@ -916,7 +888,6 @@
{
"cell_type": "code",
"execution_count": 96,
- "id": "intended-model",
"metadata": {},
"outputs": [
{
@@ -937,7 +908,6 @@
{
"cell_type": "code",
"execution_count": 97,
- "id": "liberal-brand",
"metadata": {},
"outputs": [
{
@@ -958,7 +928,6 @@
{
"cell_type": "code",
"execution_count": 98,
- "id": "quiet-midnight",
"metadata": {},
"outputs": [
{
@@ -978,7 +947,6 @@
},
{
"cell_type": "markdown",
- "id": "august-radius",
"metadata": {},
"source": [
"The Italians figured out their pizza taxonomy centuries ago, and so these delicious types of pizzas all have their own names. We’d do well to take advantage of that and give the users of our Pizza class a better interface for creating the pizza objects they crave.\n",
@@ -989,7 +957,6 @@
{
"cell_type": "code",
"execution_count": 120,
- "id": "utility-poetry",
"metadata": {},
"outputs": [],
"source": [
@@ -1012,7 +979,6 @@
{
"cell_type": "code",
"execution_count": 121,
- "id": "coordinated-capture",
"metadata": {},
"outputs": [],
"source": [
@@ -1022,7 +988,6 @@
{
"cell_type": "code",
"execution_count": 122,
- "id": "wrapped-murder",
"metadata": {},
"outputs": [
{
@@ -1042,7 +1007,6 @@
},
{
"cell_type": "markdown",
- "id": "cloudy-expense",
"metadata": {},
"source": [
"Note how I’m using the `cls` argument in the `margherita` and `prosciutto` factory methods instead of calling the `Pizza` constructor directly."
@@ -1050,7 +1014,6 @@
},
{
"cell_type": "markdown",
- "id": "amateur-guarantee",
"metadata": {},
"source": [
"This is a trick you can use to follow the [Don’t Repeat Yourself (DRY)](https://en.wikipedia.org/wiki/Don't_repeat_yourself) principle. If we decide to rename this class at some point we won’t have to remember updating the constructor name in all of the classmethod factory functions."
@@ -1058,7 +1021,6 @@
},
{
"cell_type": "markdown",
- "id": "changing-anger",
"metadata": {},
"source": [
"Now, what can we do with these factory methods? Let’s try them out:"
@@ -1067,7 +1029,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "powered-charter",
"metadata": {},
"outputs": [
{
@@ -1088,7 +1049,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "expanded-charger",
"metadata": {},
"outputs": [
{
@@ -1108,7 +1068,6 @@
},
{
"cell_type": "markdown",
- "id": "average-candle",
"metadata": {},
"source": [
"As you can see, we can use the factory functions to create new `Pizza` objects that are configured the way we want them. They all use the same `__init__` constructor internally and simply provide a shortcut for remembering all of the various ingredients."
@@ -1116,7 +1075,6 @@
},
{
"cell_type": "markdown",
- "id": "legislative-myanmar",
"metadata": {},
"source": [
"Another way to look at this use of class methods is that they allow you to define alternative constructors for your classes."
@@ -1124,7 +1082,6 @@
},
{
"cell_type": "markdown",
- "id": "civilian-muscle",
"metadata": {},
"source": [
"Python only allows one `__init__` method per class. Using class methods it’s possible to add as many alternative constructors as necessary. This can make the interface for your classes self-documenting (to a certain degree) and simplify their usage."
@@ -1132,16 +1089,15 @@
},
{
"cell_type": "markdown",
- "id": "trying-republic",
"metadata": {},
"source": [
- "### When To Use Static Methods"
+ "\n",
+ "### When To Use Static Methods [
](#table_of_contents)"
]
},
{
"cell_type": "code",
"execution_count": 19,
- "id": "according-hostel",
"metadata": {},
"outputs": [],
"source": [
@@ -1166,7 +1122,6 @@
},
{
"cell_type": "markdown",
- "id": "processed-margin",
"metadata": {},
"source": [
"As we’ve learned, static methods can’t access class or instance state because they don’t take a `cls` or `self` argument. That’s a big limitation — but it’s also a great signal to show that a particular method is independent from everything else around it."
@@ -1174,7 +1129,6 @@
},
{
"cell_type": "markdown",
- "id": "sticky-classification",
"metadata": {},
"source": [
"In the above example, it’s clear that `circle_area()` can’t modify the class or the class instance in any way. (Sure, you could always work around that with a global variable but that’s not the point here.)"
@@ -1182,7 +1136,6 @@
},
{
"cell_type": "markdown",
- "id": "destroyed-chick",
"metadata": {},
"source": [
"Now, why is that useful?\n",
@@ -1192,7 +1145,6 @@
},
{
"cell_type": "markdown",
- "id": "weird-metadata",
"metadata": {},
"source": [
"Techniques like that allow you to communicate clearly about parts of your class architecture so that new development work is naturally guided to happen within these set boundaries. Of course, it would be easy enough to defy these restrictions. But in practice they often help avoid accidental modifications going against the original design."
@@ -1200,7 +1152,6 @@
},
{
"cell_type": "markdown",
- "id": "brazilian-birthday",
"metadata": {},
"source": [
"Put differently, using static methods and class methods are ways to communicate developer intent while enforcing that intent enough to avoid most slip of the mind mistakes and bugs that would break the design."
@@ -1208,7 +1159,6 @@
},
{
"cell_type": "markdown",
- "id": "opponent-concept",
"metadata": {},
"source": [
"Static methods also have benefits when it comes to writing test code.\n",
@@ -1220,10 +1170,10 @@
},
{
"cell_type": "markdown",
- "id": "reliable-medicare",
"metadata": {},
"source": [
- "### Key Takeaways\n",
+ "\n",
+ "### Key Takeaways [
](#table_of_contents)\n",
"\n",
"- Instance methods need a class instance and can access the instance through `self`.\n",
"- Class methods don’t need a class instance. They can’t access the instance (`self`) but they have access to the class itself via `cls`.\n",
@@ -1248,7 +1198,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/11.1 Iterators.ipynb b/Python/04. Advanced/04.1 Iterators.ipynb
similarity index 90%
rename from python/04. Advanced/11.1 Iterators.ipynb
rename to Python/04. Advanced/04.1 Iterators.ipynb
index 0daa98c..d6882b8 100755
--- a/python/04. Advanced/11.1 Iterators.ipynb
+++ b/Python/04. Advanced/04.1 Iterators.ipynb
@@ -2,15 +2,37 @@
"cells": [
{
"cell_type": "markdown",
- "id": "happy-ottawa",
"metadata": {},
"source": [
- "# Iterators"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Iterators \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Iterating Through an Iterator](#iterating_through_an_iterator)\n",
+ "* [Working of for loop for Iterators](#working_of_for_loop_for_iterators)\n",
+ "* [Building Custom Iterators](#building_custom_iterators)\n",
+ "* [Python Infinite Iterators](#python_infinite_iterators)\n",
+ "* [`range` Example](#`range`_example)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "conceptual-bookmark",
"metadata": {},
"source": [
"I love how beautiful and clear Python’s syntax is compared to many other programming languages.\n",
@@ -21,7 +43,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "naval-boundary",
"metadata": {},
"outputs": [
{
@@ -42,7 +63,6 @@
},
{
"cell_type": "markdown",
- "id": "coastal-italy",
"metadata": {},
"source": [
"**But how do Python’s elegant loop constructs work behind the scenes?** How does the loop fetch individual elements from the object it is looping over? And how can you support the same programming style in your own Python objects?"
@@ -50,7 +70,6 @@
},
{
"cell_type": "markdown",
- "id": "floating-violin",
"metadata": {},
"source": [
"**You’ll find the answer to these questions in Python’s iterator protocol:**\n",
@@ -60,7 +79,6 @@
},
{
"cell_type": "markdown",
- "id": "difficult-filename",
"metadata": {},
"source": [
"But let’s take things step by step. Just like decorators, iterators and their related techniques can appear quite arcane and complicated on first glance. So we’ll ease into it.\n",
@@ -70,7 +88,6 @@
},
{
"cell_type": "markdown",
- "id": "imposed-execution",
"metadata": {},
"source": [
"Iterators are everywhere in Python. They are elegantly implemented within for loops, comprehensions, generators etc. but are hidden in plain sight. Iterator in Python is simply an object that can be iterated upon. An object which will return data, one element at a time."
@@ -78,7 +95,6 @@
},
{
"cell_type": "markdown",
- "id": "timely-farmer",
"metadata": {},
"source": [
"Technically speaking, a Python iterator object must implement two special methods, `__iter__()` and `__next__()`, collectively called the iterator protocol. An object is called iterable if we can get an iterator from it. Most built-in containers in Python like: list, tuple, string etc. are iterables.\n",
@@ -88,15 +104,14 @@
},
{
"cell_type": "markdown",
- "id": "greenhouse-disaster",
"metadata": {},
"source": [
- "## Iterating Through an Iterator"
+ "\n",
+ "## Iterating Through an Iterator [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "pleasant-correction",
"metadata": {},
"source": [
"We use the `next()` function to manually iterate through all the items of an iterator. When we reach the end and there is no more data to be returned, it will raise the `StopIteration` Exception. Following is an example."
@@ -104,8 +119,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
- "id": "innocent-egypt",
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -114,8 +128,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "id": "sophisticated-matrix",
+ "execution_count": 3,
"metadata": {},
"outputs": [
{
@@ -169,7 +182,7 @@
" 'sort']"
]
},
- "execution_count": 2,
+ "execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@@ -179,10 +192,18 @@
"dir(my_list)"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "myiter = iter(my_list)"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 96,
- "id": "herbal-davis",
"metadata": {},
"outputs": [
{
@@ -204,7 +225,6 @@
{
"cell_type": "code",
"execution_count": 97,
- "id": "foster-tumor",
"metadata": {},
"outputs": [
{
@@ -225,7 +245,6 @@
{
"cell_type": "code",
"execution_count": 98,
- "id": "wireless-console",
"metadata": {},
"outputs": [
{
@@ -247,7 +266,6 @@
{
"cell_type": "code",
"execution_count": 99,
- "id": "collected-amendment",
"metadata": {},
"outputs": [
{
@@ -268,7 +286,6 @@
{
"cell_type": "code",
"execution_count": 100,
- "id": "beginning-security",
"metadata": {},
"outputs": [
{
@@ -290,7 +307,6 @@
},
{
"cell_type": "markdown",
- "id": "mexican-exposure",
"metadata": {},
"source": [
"A more elegant way of automatically iterating is by using the for loop. Using this, we can iterate over any object that can return an iterator, for example list, string, file etc."
@@ -298,15 +314,14 @@
},
{
"cell_type": "markdown",
- "id": "prostate-resource",
"metadata": {},
"source": [
- "## Working of for loop for Iterators"
+ "\n",
+ "## Working of for loop for Iterators [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "informal-antigua",
"metadata": {},
"source": [
"As we see in the above example, the for loop was able to iterate automatically through the list."
@@ -314,7 +329,6 @@
},
{
"cell_type": "markdown",
- "id": "outside-carrier",
"metadata": {},
"source": [
"In fact the `for` loop can iterate over any iterable. Let's take a closer look at how the `for` loop is actually implemented in Python.\n",
@@ -344,7 +358,6 @@
},
{
"cell_type": "markdown",
- "id": "seasonal-cleanup",
"metadata": {},
"source": [
"So internally, the for loop creates an iterator object, `iter_obj` by calling `iter()` on the iterable."
@@ -352,7 +365,6 @@
},
{
"cell_type": "markdown",
- "id": "protecting-parent",
"metadata": {},
"source": [
"Ironically, this `for` loop is actually an infinite while loop."
@@ -360,7 +372,6 @@
},
{
"cell_type": "markdown",
- "id": "acknowledged-style",
"metadata": {},
"source": [
"Inside the loop, it calls `next()` to get the next element and executes the body of the `for` loop with this value. After all the items exhaust, `StopIteration` is raised which is internally caught and the loop ends. Note that any other kind of exception will pass through."
@@ -368,15 +379,14 @@
},
{
"cell_type": "markdown",
- "id": "together-walter",
"metadata": {},
"source": [
- "## Building Custom Iterators"
+ "\n",
+ "## Building Custom Iterators [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "crucial-nancy",
"metadata": {},
"source": [
"Building an iterator from scratch is easy in Python. We just have to implement the `__iter__()` and the `__next__()` methods."
@@ -384,7 +394,6 @@
},
{
"cell_type": "markdown",
- "id": "pointed-month",
"metadata": {},
"source": [
"The `__iter__()` method returns the iterator object itself. If required, some initialization can be performed."
@@ -392,7 +401,6 @@
},
{
"cell_type": "markdown",
- "id": "under-connectivity",
"metadata": {},
"source": [
"The `__next__()` method must return the next item in the sequence. On reaching the end, and in subsequent calls, it must raise `StopIteration`."
@@ -400,7 +408,6 @@
},
{
"cell_type": "markdown",
- "id": "incident-caribbean",
"metadata": {},
"source": [
"Here, we show an example that will give us the next power of 2 in each iteration. Power exponent starts from zero up to a user set number."
@@ -408,7 +415,6 @@
},
{
"cell_type": "markdown",
- "id": "bacterial-vacation",
"metadata": {},
"source": [
"If you do not have any idea about object-oriented programming, visit Python Object-Oriented Programming."
@@ -417,7 +423,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "boolean-politics",
"metadata": {},
"outputs": [],
"source": [
@@ -431,7 +436,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "motivated-teens",
"metadata": {},
"outputs": [],
"source": [
@@ -451,7 +455,6 @@
{
"cell_type": "code",
"execution_count": 82,
- "id": "humanitarian-salem",
"metadata": {},
"outputs": [],
"source": [
@@ -466,7 +469,6 @@
" self.n = 0\n",
" return self\n",
" \n",
- " \n",
" def __next__(self):\n",
" print('calling next...')\n",
" current = self.n\n",
@@ -481,7 +483,6 @@
{
"cell_type": "code",
"execution_count": 84,
- "id": "integrated-accordance",
"metadata": {},
"outputs": [],
"source": [
@@ -491,7 +492,6 @@
{
"cell_type": "code",
"execution_count": 71,
- "id": "tutorial-violence",
"metadata": {},
"outputs": [
{
@@ -519,7 +519,6 @@
{
"cell_type": "code",
"execution_count": 72,
- "id": "caroline-chuck",
"metadata": {},
"outputs": [
{
@@ -540,7 +539,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "ongoing-imperial",
"metadata": {},
"outputs": [],
"source": [
@@ -567,7 +565,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "psychological-initial",
"metadata": {},
"outputs": [],
"source": [
@@ -578,7 +575,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "universal-stomach",
"metadata": {},
"outputs": [],
"source": [
@@ -589,7 +585,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "happy-copyright",
"metadata": {},
"outputs": [
{
@@ -611,7 +606,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "medium-quantity",
"metadata": {},
"outputs": [
{
@@ -632,7 +626,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "artistic-labor",
"metadata": {},
"outputs": [
{
@@ -653,7 +646,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "corporate-metallic",
"metadata": {},
"outputs": [
{
@@ -674,7 +666,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "little-judge",
"metadata": {},
"outputs": [
{
@@ -696,7 +687,6 @@
},
{
"cell_type": "markdown",
- "id": "olympic-scanner",
"metadata": {},
"source": [
"We can also use a `for` loop to iterate over our iterator class."
@@ -705,7 +695,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "sporting-phenomenon",
"metadata": {},
"outputs": [
{
@@ -728,15 +717,14 @@
},
{
"cell_type": "markdown",
- "id": "unlikely-making",
"metadata": {},
"source": [
- "## Python Infinite Iterators"
+ "\n",
+ "## Python Infinite Iterators [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "consecutive-messenger",
"metadata": {},
"source": [
"It is not necessary that the item in an iterator object has to be exhausted. There can be infinite iterators (which never ends). We must be careful when handling such iterators."
@@ -744,7 +732,6 @@
},
{
"cell_type": "markdown",
- "id": "mighty-prior",
"metadata": {},
"source": [
"Here is a simple example to demonstrate infinite iterators."
@@ -752,7 +739,6 @@
},
{
"cell_type": "markdown",
- "id": "colored-essence",
"metadata": {},
"source": [
"The built-in function `iter()` can be called with two arguments where the first argument must be a callable object (function) and second is the sentinel. The iterator calls this function until the returned value is equal to the sentinel."
@@ -761,7 +747,6 @@
{
"cell_type": "code",
"execution_count": 301,
- "id": "advance-manitoba",
"metadata": {},
"outputs": [
{
@@ -782,7 +767,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "hollow-container",
"metadata": {},
"outputs": [],
"source": [
@@ -792,7 +776,6 @@
{
"cell_type": "code",
"execution_count": 60,
- "id": "caring-words",
"metadata": {},
"outputs": [
{
@@ -813,7 +796,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "crazy-geometry",
"metadata": {},
"outputs": [
{
@@ -833,7 +815,6 @@
},
{
"cell_type": "markdown",
- "id": "popular-trick",
"metadata": {},
"source": [
"We can see that the `int()` function always returns `0`. So passing it as `iter(int, 1)` will return an iterator that calls `int()` until the returned value equals `1`. This never happens and we get an infinite iterator."
@@ -841,7 +822,6 @@
},
{
"cell_type": "markdown",
- "id": "joined-concord",
"metadata": {},
"source": [
"We can also build our own infinite iterators. The following iterator will, theoretically, return all the odd numbers."
@@ -849,8 +829,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
- "id": "floral-progressive",
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -870,7 +849,6 @@
},
{
"cell_type": "markdown",
- "id": "beneficial-casting",
"metadata": {},
"source": [
"A sample run would be as follows."
@@ -878,8 +856,7 @@
},
{
"cell_type": "code",
- "execution_count": 64,
- "id": "fancy-thesaurus",
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@@ -888,8 +865,7 @@
},
{
"cell_type": "code",
- "execution_count": 65,
- "id": "internal-diana",
+ "execution_count": 8,
"metadata": {},
"outputs": [
{
@@ -898,7 +874,7 @@
"1"
]
},
- "execution_count": 65,
+ "execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@@ -909,8 +885,7 @@
},
{
"cell_type": "code",
- "execution_count": 66,
- "id": "cheap-physiology",
+ "execution_count": 9,
"metadata": {},
"outputs": [
{
@@ -919,7 +894,7 @@
"3"
]
},
- "execution_count": 66,
+ "execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@@ -930,8 +905,7 @@
},
{
"cell_type": "code",
- "execution_count": 67,
- "id": "prospective-shuttle",
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
@@ -940,7 +914,7 @@
"5"
]
},
- "execution_count": 67,
+ "execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
@@ -951,17 +925,16 @@
},
{
"cell_type": "code",
- "execution_count": 68,
- "id": "superb-excellence",
+ "execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "7"
+ "83"
]
},
- "execution_count": 68,
+ "execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
@@ -972,7 +945,6 @@
},
{
"cell_type": "markdown",
- "id": "permanent-saint",
"metadata": {},
"source": [
"And so on...\n",
@@ -982,7 +954,6 @@
},
{
"cell_type": "markdown",
- "id": "continental-clerk",
"metadata": {},
"source": [
"The advantage of using iterators is that they save resources. Like shown above, we could get all the odd numbers without storing the entire number system in memory. We can have infinite items (theoretically) in finite memory."
@@ -990,15 +961,14 @@
},
{
"cell_type": "markdown",
- "id": "varying-click",
"metadata": {},
"source": [
- "## `range` Example"
+ "\n",
+ "## `range` Example [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "wanted-retailer",
"metadata": {},
"source": [
"We can write the famous `range` iterator now."
@@ -1007,7 +977,6 @@
{
"cell_type": "code",
"execution_count": 131,
- "id": "least-sleep",
"metadata": {},
"outputs": [],
"source": [
@@ -1032,7 +1001,6 @@
{
"cell_type": "code",
"execution_count": 139,
- "id": "latter-essex",
"metadata": {},
"outputs": [],
"source": [
@@ -1042,7 +1010,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "wired-nursing",
"metadata": {},
"outputs": [
{
@@ -1063,7 +1030,6 @@
{
"cell_type": "code",
"execution_count": 141,
- "id": "imported-building",
"metadata": {},
"outputs": [
{
@@ -1084,7 +1050,6 @@
{
"cell_type": "code",
"execution_count": 142,
- "id": "knowing-stephen",
"metadata": {},
"outputs": [
{
@@ -1105,7 +1070,6 @@
{
"cell_type": "code",
"execution_count": 144,
- "id": "prostate-mattress",
"metadata": {},
"outputs": [
{
@@ -1126,7 +1090,6 @@
{
"cell_type": "code",
"execution_count": 145,
- "id": "supposed-clothing",
"metadata": {},
"outputs": [
{
@@ -1163,7 +1126,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/Python/04. Advanced/04.2 Generators.ipynb b/Python/04. Advanced/04.2 Generators.ipynb
new file mode 100755
index 0000000..be01ec8
--- /dev/null
+++ b/Python/04. Advanced/04.2 Generators.ipynb
@@ -0,0 +1,923 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Generators \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Using Generators](#using_generators)\n",
+ " * [Example 1: Reading Large Files](#example_1:_reading_large_files)\n",
+ " * [Example 2: Generating an Infinite Sequence](#example_2:_generating_an_infinite_sequence)\n",
+ "* [Understanding Generators](#understanding_generators)\n",
+ "* [Building Generators With Generator Expressions](#building_generators_with_generator_expressions)\n",
+ "* [Profiling Generator Performance](#profiling_generator_performance)\n",
+ "* [Understanding the Python Yield Statement](#understanding_the_python_yield_statement)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Have you ever had to work with a dataset so large that it overwhelmed your machine’s memory? Or maybe you have a complex function that needs to maintain an internal state every time it’s called, but the function is too small to justify creating its own class. In these cases and more, generators and the Python yield statement are here to help."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you’re a beginner or intermediate Pythonista and you’re interested in learning how to work with large datasets in a more Pythonic fashion, then this is the tutorial for you."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Using Generators [
](#table_of_contents)\n",
+ "\n",
+ "Introduced with [PEP 255](https://www.python.org/dev/peps/pep-0255), generator functions are a special kind of function that return a lazy iterator. These are objects that you can loop over like a list. However, unlike lists, lazy iterators do not store their contents in memory. For an overview of iterators in Python, take a look at Python “for” Loops (Definite Iteration).\n",
+ "\n",
+ "Now that you have a rough idea of what a generator does, you might wonder what they look like in action. Let’s take a look at two examples. In the first, you’ll see how generators work from a bird’s eye view. Then, you’ll zoom in and examine each example more thoroughly."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Example 1: Reading Large Files [
](#table_of_contents)\n",
+ "\n",
+ "A common use case of generators is to work with data streams or large files.\n",
+ "\n",
+ "These text files separate data into columns by using commas. This format is a common way to share data. Now, what if you want to count the number of rows in a text file? The code block below shows one way of counting those rows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from tqdm import tqdm"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def text_file_reader(file_path):\n",
+ " return open(file_path)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "file_path = \"../files/some_large_file.txt\"\n",
+ "with open(file_path, \"w\") as f:\n",
+ " for i in range(1000000):\n",
+ " f.write(f\"this is line {i+1}\\n\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100000000it [00:24, 4065950.25it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Row count is 100000000\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "text_gen = text_file_reader(file_path)\n",
+ "row_count = 0\n",
+ "\n",
+ "for row in tqdm(text_gen):\n",
+ " row_count += 1\n",
+ "\n",
+ "print(f\"Row count is {row_count}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Looking at this example, you might expect `text_gen` to be a list. To populate this list, `open()` opens a file and loads its contents into `text_gen`. Then, the program iterates over the list and increments `row_count` for each row."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is a reasonable explanation, but would this design still work if the file is very large? What if the file is larger than the memory you have available? To answer this question, let’s assume that `text_file_reader()` just opens the file and reads it into an array:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def text_file_reader(file_path):\n",
+ " file = open(file_path)\n",
+ " result = file.read().split(\"\\n\")\n",
+ " return result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This function opens a given file and uses `file.read()` along with `.split()` to add each line as a separate element to a list. If you were to use this version of `csv_reader()` in the row counting code block you saw further up, then you’d get the following output:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "lines = text_file_reader(file_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this case, `open()` returns a generator object that you can lazily iterate through line by line. However, `file.read().split()` loads everything into memory at once, causing the `MemoryError`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before that happens, you’ll probably notice your computer slow to a crawl. You might even need to kill the program with a `KeyboardInterrupt`. So, how can you handle these huge data files? Take a look at a new definition of `text_file_reader()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def text_file_reader(file_path):\n",
+ " for row in open(file_path, \"r\"):\n",
+ " yield row"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this version, you `open` the file, iterate through it, and `yield` a row. This code should produce the following output, with no memory errors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "text_file_reader(file_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What’s happening here? Well, you’ve essentially turned `text_file_reader()` into a generator function. This version opens a file, loops through each line, and yields each row, instead of returning it."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can also define a generator expression (also called a generator comprehension), which has a very similar syntax to list comprehensions. In this way, you can use the generator without calling a function:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "text_gen = (row for row in open(file_path))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ " at 0x7f3d4ad86150>"
+ ]
+ },
+ "execution_count": 42,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "text_gen"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is a more succinct way to create the list `text_gen`. You’ll learn more about the Python `yield` statement soon. For now, just remember this key difference:\n",
+ "\n",
+ "- Using `yield` will result in a generator object.\n",
+ "- Using `return` will result in the first line of the file only."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Example 2: Generating an Infinite Sequence [
](#table_of_contents)\n",
+ "\n",
+ "Let’s switch gears and look at infinite sequence generation. In Python, to get a finite sequence, you call `range()` and evaluate it in a list context:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0, 1, 2, 3, 4]"
+ ]
+ },
+ "execution_count": 45,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "a = range(5)\n",
+ "list(a)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "gen = zip([1, 2, 3], [4, 5, 6])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 81,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def my_gen(mylist):\n",
+ " for item in mylist:\n",
+ " yield item**2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 82,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n",
+ "gen = my_gen(x)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 83,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1"
+ ]
+ },
+ "execution_count": 83,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "next(gen)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Generating an infinite sequence, however, will require the use of a generator, since your computer memory is finite:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 133,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def infinite_sequence():\n",
+ " num = 0\n",
+ " while True:\n",
+ " yield num\n",
+ " num += 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This code block is short and sweet. First, you initialize the variable `num` and start an infinite loop. Then, you immediately `yield num` so that you can capture the initial state. This mimics the action of `range()`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "After `yield`, you increment `num` by 1. If you try this with a for loop, then you’ll see that it really does seem infinite:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for i in infinite_sequence():\n",
+ " print(i, end=\" \")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The program will continue to execute until you stop it manually."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Instead of using a for loop, you can also call `next()` on the generator object directly. This is especially useful for testing a generator in the console:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0"
+ ]
+ },
+ "execution_count": 48,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "gen = infinite_sequence()\n",
+ "next(gen)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1"
+ ]
+ },
+ "execution_count": 49,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "next(gen)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "2"
+ ]
+ },
+ "execution_count": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "next(gen)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, you have a generator called `gen`, which you manually iterate over by repeatedly calling `next()`. This works as a great sanity check to make sure your generators are producing the output you expect."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Note:** When you use `next()`, Python calls `.__next__()` on the function you pass in as a parameter. There are some special effects that this parameterization allows, but it goes beyond the scope of this article. Experiment with changing the parameter you pass to `next()` and see what happens!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Understanding Generators [
](#table_of_contents)\n",
+ "\n",
+ "So far, you’ve learned about the two primary ways of creating generators: by using generator functions and generator expressions. You might even have an intuitive understanding of how generators work. Let’s take a moment to make that knowledge a little more explicit."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Generator functions look and act just like regular functions, but with one defining characteristic. Generator functions use the Python `yield` keyword instead of return. Recall the generator function you wrote earlier:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def infinite_sequence():\n",
+ " num = 0\n",
+ " while True:\n",
+ " yield num\n",
+ " num += 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This looks like a typical function definition, except for the Python `yield` statement and the code that follows it. `yield` indicates where a value is sent back to the caller, but unlike `return`, you don’t exit the function afterward."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Instead, the **state** of the function is remembered. That way, when `next()` is called on a generator object (either explicitly or implicitly within a `for` loop), the previously yielded variable `num` is incremented, and then yielded again. Since generator functions look like other functions and act very similarly to them, you can assume that generator expressions are very similar to other comprehensions available in Python."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Building Generators With Generator Expressions [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Like list comprehensions, generator expressions allow you to quickly create a generator object in just a few lines of code. They’re also useful in the same cases where list comprehensions are used, with an added benefit: you can create them without building and holding the entire object in memory before iteration. In other words, you’ll have no memory penalty when you use generator expressions. Take this example of squaring some numbers:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "nums_squared_lc = [num**2 for num in range(5)]\n",
+ "nums_squared_gc = (num**2 for num in range(5))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Both `nums_squared_lc` and `nums_squared_gc` look basically the same, but there’s one key difference. Can you spot it? Take a look at what happens when you inspect each of these objects:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[0, 1, 4, 9, 16]"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "nums_squared_lc"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ " at 0x7f9143561f50>"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "nums_squared_gc"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The first object used brackets to build a list, while the second created a generator expression by using parentheses. The output confirms that you’ve created a generator object and that it is distinct from a list."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Profiling Generator Performance [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You learned earlier that generators are a great way to optimize memory. While an infinite sequence generator is an extreme example of this optimization, let’s amp up the number squaring examples you just saw and inspect the size of the resulting objects. You can do this with a call to `sys.getsizeof()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "87632"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import sys\n",
+ "nums_squared_lc = [i * 2 for i in range(10000)]\n",
+ "sys.getsizeof(nums_squared_lc)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "128"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "nums_squared_gc = (i ** 2 for i in range(10000))\n",
+ "sys.getsizeof(nums_squared_gc)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this case, the list you get from the list comprehension is 87,624 bytes, while the generator object is only 120. This means that the list is over 700 times larger than the generator object!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There is one thing to keep in mind, though. If the list is smaller than the running machine’s available memory, then list comprehensions can be faster to evaluate than the equivalent generator expression because of the overhead of function calls. To explore this, let’s sum across the results from the two comprehensions above. You can generate a readout with `cProfile.run()`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 5 function calls in 0.001 seconds\n",
+ "\n",
+ " Ordered by: standard name\n",
+ "\n",
+ " ncalls tottime percall cumtime percall filename:lineno(function)\n",
+ " 1 0.001 0.001 0.001 0.001 :1()\n",
+ " 1 0.000 0.000 0.001 0.001 :1()\n",
+ " 1 0.000 0.000 0.001 0.001 {built-in method builtins.exec}\n",
+ " 1 0.000 0.000 0.000 0.000 {built-in method builtins.sum}\n",
+ " 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}\n",
+ "\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "import cProfile\n",
+ "cProfile.run('sum([i * 2 for i in range(10000)])')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 10005 function calls in 0.002 seconds\n",
+ "\n",
+ " Ordered by: standard name\n",
+ "\n",
+ " ncalls tottime percall cumtime percall filename:lineno(function)\n",
+ " 10001 0.001 0.000 0.001 0.000 :1()\n",
+ " 1 0.000 0.000 0.002 0.002 :1()\n",
+ " 1 0.000 0.000 0.002 0.002 {built-in method builtins.exec}\n",
+ " 1 0.001 0.001 0.002 0.002 {built-in method builtins.sum}\n",
+ " 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}\n",
+ "\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "cProfile.run('sum((i * 2 for i in range(10000)))')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here, you can see that summing across all values in the list comprehension took about a third of the time as summing across the generator. If speed is an issue and memory isn’t, then a list comprehension is likely a better tool for the job."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Remember, list comprehensions return full lists, while generator expressions return generators. Generators work the same whether they’re built from a function or an expression. Using an expression just allows you to define simple generators in a single line, with an assumed `yield` at the end of each inner iteration."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Python `yield` statement is certainly the linchpin on which all of the functionality of generators rests, so let’s dive into how `yield` works in Python."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Understanding the Python Yield Statement [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "On the whole, `yield` is a fairly simple statement. Its primary job is to control the flow of a generator function in a way that’s similar to `return` statements. As briefly mentioned above, though, the Python yield statement has a few tricks up its sleeve."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you call a generator function or use a generator expression, you return a special iterator called a generator. You can assign this generator to a variable in order to use it. When you call special methods on the generator, such as `next()`, the code within the function is executed up to yield."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When the Python `yield` statement is hit, the program suspends function execution and returns the yielded value to the caller. (In contrast, `return` stops function execution completely.) When a function is suspended, the state of that function is saved. This includes any variable bindings local to the generator, the instruction pointer, the internal stack, and any exception handling."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This allows you to resume function execution whenever you call one of the generator’s methods. In this way, all function evaluation picks back up right after `yield`. You can see this in action by using multiple Python yield statements:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def multi_yield():\n",
+ " yield_str = \"This will print the first string\"\n",
+ " yield yield_str\n",
+ " yield_str = \"This will print the second string\"\n",
+ " yield yield_str"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'This will print the first string'"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "multi_obj = multi_yield()\n",
+ "next(multi_obj)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'This will print the second string'"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "next(multi_obj)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "ename": "StopIteration",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmulti_obj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mStopIteration\u001b[0m: "
+ ]
+ }
+ ],
+ "source": [
+ "next(multi_obj)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Take a closer look at that last call to `next()`. You can see that execution has blown up with a traceback. This is because generators, like all iterators, can be exhausted. Unless your generator is infinite, you can iterate through it one time only. Once all values have been evaluated, iteration will stop and the for loop will exit. If you used `next()`, then instead you’ll get an explicit `StopIteration` exception."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`yield` can be used in many ways to control your generator’s execution flow. The use of multiple Python `yield` statements can be leveraged as far as your creativity allows."
+ ]
+ }
+ ],
+ "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.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/python/04. Advanced/11.3 Itertools.ipynb b/Python/04. Advanced/04.3 Itertools.ipynb
similarity index 83%
rename from python/04. Advanced/11.3 Itertools.ipynb
rename to Python/04. Advanced/04.3 Itertools.ipynb
index f74a6a6..3d6b847 100755
--- a/python/04. Advanced/11.3 Itertools.ipynb
+++ b/Python/04. Advanced/04.3 Itertools.ipynb
@@ -2,15 +2,40 @@
"cells": [
{
"cell_type": "markdown",
- "id": "addressed-solution",
"metadata": {},
"source": [
- "# Itertools"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Itertools \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [What Is `itertools` and Why Should You Use It?](#what_is_`itertools`_and_why_should_you_use_it?)\n",
+ "* [The `grouper` Recipe](#the_`grouper`_recipe)\n",
+ "* [Et tu, Brute Force?](#et_tu,_brute_force?)\n",
+ "* [Sequences of Numbers](#sequences_of_numbers)\n",
+ " * [Evens and Odds](#evens_and_odds)\n",
+ "* [Recurrence Relations](#recurrence_relations)\n",
+ "* [First Order Recurrence Relations](#first_order_recurrence_relations)\n",
+ "* [Second Order Recurrence Relations](#second_order_recurrence_relations)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "middle-manhattan",
"metadata": {},
"source": [
"It has been called a “gem” and “pretty much the coolest thing ever,” and if you have not heard of it, then you are missing out on one of the greatest corners of the Python 3 standard library: `itertools`."
@@ -18,7 +43,6 @@
},
{
"cell_type": "markdown",
- "id": "agreed-capability",
"metadata": {},
"source": [
"A handful of excellent resources exist for learning what functions are available in the itertools module. The docs themselves are a great place to start. So is this post."
@@ -26,7 +50,6 @@
},
{
"cell_type": "markdown",
- "id": "separated-pocket",
"metadata": {},
"source": [
"The thing about `itertools`, though, is that it is not enough to just know the definitions of the functions it contains. The real power lies in composing these functions to create fast, memory-efficient, and good-looking code."
@@ -34,7 +57,6 @@
},
{
"cell_type": "markdown",
- "id": "forward-aluminum",
"metadata": {},
"source": [
"This article takes a different approach. Rather than introducing itertools to you one function at a time, you will construct practical examples designed to encourage you to “think iteratively.” In general, the examples will start simple and gradually increase in complexity."
@@ -42,15 +64,14 @@
},
{
"cell_type": "markdown",
- "id": "false-soccer",
"metadata": {},
"source": [
- "## What Is `itertools` and Why Should You Use It?"
+ "\n",
+ "## What Is `itertools` and Why Should You Use It? [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "integrated-hindu",
"metadata": {},
"source": [
"According to the [itertools docs](https://docs.python.org/3/library/itertools.html), it is a “module [that] implements a number of `iterator` building blocks inspired by constructs from APL, Haskell, and SML… Together, they form an ‘iterator algebra’ making it possible to construct specialized tools succinctly and efficiently in pure Python.”\n",
@@ -61,7 +82,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "coupled-necessity",
"metadata": {},
"outputs": [
{
@@ -81,7 +101,6 @@
},
{
"cell_type": "markdown",
- "id": "suffering-utilization",
"metadata": {},
"source": [
"How, exactly, does `zip()` work?\n",
@@ -94,7 +113,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "multiple-blowing",
"metadata": {},
"outputs": [
{
@@ -114,7 +132,6 @@
},
{
"cell_type": "markdown",
- "id": "center-gnome",
"metadata": {},
"source": [
"Under the hood, the `zip()` function works, in essence, by calling `iter()` on each of its arguments, then advancing each iterator returned by `iter()` with `next()` and aggregating the results into tuples. The iterator returned by `zip()` iterates over these tuples."
@@ -122,7 +139,6 @@
},
{
"cell_type": "markdown",
- "id": "distant-image",
"metadata": {},
"source": [
"The `map()` built-in function is another “iterator operator” that, in its simplest form, applies a single-parameter function to each element of an iterable one element at a time:"
@@ -131,7 +147,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "equal-westminster",
"metadata": {},
"outputs": [
{
@@ -151,7 +166,6 @@
},
{
"cell_type": "markdown",
- "id": "square-chrome",
"metadata": {},
"source": [
"The `map()` function works by calling `iter()` on its second argument, advancing this iterator with `next()` until the iterator is exhausted, and applying the function passed to its first argument to the value returned by `next()` at each step. In the above example, `len()` is called on each element of `['abc', 'de', 'fghi']` to return an iterator over the lengths of each string in the list."
@@ -159,7 +173,6 @@
},
{
"cell_type": "markdown",
- "id": "cheap-monroe",
"metadata": {},
"source": [
"Since iterators are iterable, you can compose `zip()` and `map()` to produce an iterator over combinations of elements in more than one iterable. For example, the following sums corresponding elements of two lists:"
@@ -168,7 +181,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "forward-battle",
"metadata": {},
"outputs": [
{
@@ -188,7 +200,6 @@
},
{
"cell_type": "markdown",
- "id": "czech-maintenance",
"metadata": {},
"source": [
"This is what is meant by the functions in itertools forming an “iterator algebra.” itertools is best viewed as a collection of building blocks that can be combined to form specialized “data pipelines” like the one in the example above."
@@ -196,7 +207,6 @@
},
{
"cell_type": "markdown",
- "id": "designed-mongolia",
"metadata": {},
"source": [
"There are two main reasons why such an “iterator algebra” is useful: improved memory efficiency (via lazy evaluation) and faster execuction time. To see this, consider the following problem:"
@@ -204,7 +214,6 @@
},
{
"cell_type": "markdown",
- "id": "behind-haven",
"metadata": {},
"source": [
"Given a list of values inputs and a positive integer `n`, write a function that splits inputs into groups of length `n`. For simplicity, assume that the length of the input list is divisible by `n`. For example, if `inputs = [1, 2, 3, 4, 5, 6]` and `n = 2`, your function should return `[(1, 2), (3, 4), (5, 6)]`."
@@ -212,7 +221,6 @@
},
{
"cell_type": "markdown",
- "id": "settled-check",
"metadata": {},
"source": [
"Taking a naive approach, you might write something like this:"
@@ -221,7 +229,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "senior-contest",
"metadata": {},
"outputs": [],
"source": [
@@ -232,7 +239,6 @@
},
{
"cell_type": "markdown",
- "id": "subjective-ordinance",
"metadata": {},
"source": [
"When you test it, you see that it works as expected:"
@@ -241,7 +247,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "criminal-accordance",
"metadata": {},
"outputs": [
{
@@ -262,7 +267,6 @@
},
{
"cell_type": "markdown",
- "id": "solved-result",
"metadata": {},
"source": [
"What happens when you try to pass it a list with, say, 100 million elements? You will need a whole lot of available memory! Even if you have enough memory available, your program will hang for a while until the output list is populated."
@@ -270,7 +274,6 @@
},
{
"cell_type": "markdown",
- "id": "surprised-borough",
"metadata": {},
"source": [
"To see this, you can use the `%memit` command (on UNIX systems) to measure memory usage. **Make sure you have at least 5GB of free memory before executing the following**:\n",
@@ -281,7 +284,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "tracked-bronze",
"metadata": {},
"outputs": [],
"source": [
@@ -293,7 +295,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "pressed-spring",
"metadata": {},
"outputs": [],
"source": [
@@ -304,7 +305,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "foreign-certificate",
"metadata": {},
"outputs": [
{
@@ -323,7 +323,6 @@
},
{
"cell_type": "markdown",
- "id": "waiting-membrane",
"metadata": {},
"source": [
"The list and tuple implementation in `naive_grouper()` requires approximately **4.5GB** of memory to process `range(100000000)`. Working with iterators drastically improves this situation. Consider the following:"
@@ -332,7 +331,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "driving-grove",
"metadata": {},
"outputs": [],
"source": [
@@ -343,7 +341,6 @@
},
{
"cell_type": "markdown",
- "id": "aging-carnival",
"metadata": {},
"source": [
"There’s a lot going on in this little function, so let’s break it down with a concrete example. The expression [iters(inputs)] * n creates a list of n references to the same iterator:"
@@ -352,7 +349,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "coordinated-holmes",
"metadata": {},
"outputs": [
{
@@ -374,7 +370,6 @@
},
{
"cell_type": "markdown",
- "id": "returning-poultry",
"metadata": {},
"source": [
"Next, `zip(*iters)` returns an iterator over pairs of corresponding elements of each iterator in iters. When the first element, 1, is taken from the “first” iterator, the “second” iterator now starts at 2 since it is just a reference to the “first” iterator and has therefore been advanced one step. So, the first tuple produced by `zip()` is `(1, 2)`."
@@ -382,7 +377,6 @@
},
{
"cell_type": "markdown",
- "id": "developed-raleigh",
"metadata": {},
"source": [
"At this point, “both” iterators in iters start at 3, so when `zip()` pulls 3 from the “first” iterator, it gets 4 from the “second” to produce the tuple `(3, 4)`. This process continues until `zip()` finally produces `(9, 10)` and “both” iterators in iters are exhausted:"
@@ -391,7 +385,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "flush-adolescent",
"metadata": {},
"outputs": [
{
@@ -412,7 +405,6 @@
},
{
"cell_type": "markdown",
- "id": "solved-prediction",
"metadata": {},
"source": [
"The `better_grouper()` function is better for a couple of reasons. First, without the reference to the `len()` built-in, `better_grouper()` can take any iterable as an argument (even infinite iterators). Second, by returning an iterator rather than a list, `better_grouper()` can process enormous iterables without trouble and uses much less memory."
@@ -420,7 +412,6 @@
},
{
"cell_type": "markdown",
- "id": "occupational-angel",
"metadata": {},
"source": [
"Run the following again:"
@@ -429,7 +420,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "third-concrete",
"metadata": {},
"outputs": [],
"source": [
@@ -441,7 +431,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "exceptional-rhythm",
"metadata": {},
"outputs": [
{
@@ -460,7 +449,6 @@
},
{
"cell_type": "markdown",
- "id": "persistent-terry",
"metadata": {},
"source": [
"That’s a whopping 630 times less memory used before in less than a quarter of the time!"
@@ -468,7 +456,6 @@
},
{
"cell_type": "markdown",
- "id": "growing-engine",
"metadata": {},
"source": [
"Now that you’ve seen what `itertools` is (“iterator algebra”) and why you should use it (improved memory efficiency and faster execution time), let’s take a look at how to take `better_grouper()` to the next level with itertools."
@@ -476,15 +463,14 @@
},
{
"cell_type": "markdown",
- "id": "municipal-sessions",
"metadata": {},
"source": [
- "## The `grouper` Recipe"
+ "\n",
+ "## The `grouper` Recipe [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "needed-blanket",
"metadata": {},
"source": [
"The problem with `better_grouper()` is that it doesn’t handle situations where the value passed to the second argument isn’t a factor of the length of the iterable in the first argument:"
@@ -493,7 +479,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "contained-wisdom",
"metadata": {},
"outputs": [
{
@@ -514,7 +499,6 @@
},
{
"cell_type": "markdown",
- "id": "taken-canada",
"metadata": {},
"source": [
"The elements 9 and 10 are missing from the grouped output. This happens because `zip()` stops aggregating elements once the shortest iterable passed to it is exhausted. It would make more sense to return a third group containing 9 and 10."
@@ -522,7 +506,6 @@
},
{
"cell_type": "markdown",
- "id": "expired-burner",
"metadata": {},
"source": [
"To do this, you can use `itertools.zip_longest()`. This function accepts any number of iterables as arguments and a fillvalue keyword argument that defaults to None. The easiest way to get a sense of the difference between `zip()` and `zip_longest()` is to look at some example output:"
@@ -531,7 +514,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "extra-canyon",
"metadata": {},
"outputs": [],
"source": [
@@ -541,7 +523,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "australian-drive",
"metadata": {},
"outputs": [],
"source": [
@@ -552,7 +533,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "secure-aggregate",
"metadata": {},
"outputs": [
{
@@ -572,7 +552,6 @@
},
{
"cell_type": "markdown",
- "id": "executive-playing",
"metadata": {},
"source": [
"With this in mind, replace zip() in better_grouper() with zip_longest():"
@@ -581,7 +560,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "psychological-maldives",
"metadata": {},
"outputs": [],
"source": [
@@ -595,7 +573,6 @@
},
{
"cell_type": "markdown",
- "id": "narrow-bench",
"metadata": {},
"source": [
"Now you get a better result:"
@@ -604,7 +581,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "mineral-village",
"metadata": {},
"outputs": [
{
@@ -622,7 +598,6 @@
},
{
"cell_type": "markdown",
- "id": "split-antarctica",
"metadata": {},
"source": [
"The `grouper()` function can be found in the [Recipes section](https://docs.python.org/3.6/library/itertools.html#itertools-recipes) of the itertools docs. The recipes are an excellent source of inspiration for ways to use itertools to your advantage."
@@ -630,15 +605,14 @@
},
{
"cell_type": "markdown",
- "id": "instrumental-creativity",
"metadata": {},
"source": [
- "## Et tu, Brute Force?"
+ "\n",
+ "## Et tu, Brute Force? [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "verified-compound",
"metadata": {},
"source": [
"Here’s a common interview-style problem:\n",
@@ -648,7 +622,6 @@
},
{
"cell_type": "markdown",
- "id": "blank-referral",
"metadata": {},
"source": [
"To “brute force” this problem, you just start listing off the ways there are to choose one bill from your wallet, check whether any of these makes change for $100, then list the ways to pick two bills from your wallet, check again, and so on and so forth."
@@ -656,7 +629,6 @@
},
{
"cell_type": "markdown",
- "id": "infrared-flood",
"metadata": {},
"source": [
"But you are a programmer, so naturally you want to automate this process."
@@ -664,7 +636,6 @@
},
{
"cell_type": "markdown",
- "id": "improved-evaluation",
"metadata": {},
"source": [
"First, create a list of the bills you have in your wallet:"
@@ -673,7 +644,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "partial-compensation",
"metadata": {},
"outputs": [],
"source": [
@@ -683,7 +653,6 @@
{
"cell_type": "code",
"execution_count": 33,
- "id": "educated-debate",
"metadata": {},
"outputs": [
{
@@ -1703,7 +1672,6 @@
},
{
"cell_type": "markdown",
- "id": "color-relevance",
"metadata": {},
"source": [
"A choice of `k` things from a set of `n` things is called a **combination**, and `itertools` has your back here. The `itertools.combinations()` function takes two arguments—an iterable inputs and a positive integer `n`—and produces an iterator over tuples of all combinations of `n` elements in inputs."
@@ -1711,7 +1679,6 @@
},
{
"cell_type": "markdown",
- "id": "driven-rings",
"metadata": {},
"source": [
"For example, to list the combinations of three bills in your wallet, just do:"
@@ -1719,473 +1686,19 @@
},
{
"cell_type": "code",
- "execution_count": 33,
- "id": "anonymous-missile",
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
- "data": {
- "text/plain": [
- "[(20, 20, 20),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 5),\n",
- " (20, 20, 5),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 5),\n",
- " (20, 20, 5),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 5, 5),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 10),\n",
- " (20, 20, 5),\n",
- " (20, 20, 5),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 20, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 5, 5),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 10),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 5),\n",
- " (20, 10, 5),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 10, 1),\n",
- " (20, 5, 5),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 5, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (20, 1, 1),\n",
- " (10, 10, 10),\n",
- " (10, 10, 10),\n",
- " (10, 10, 10),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 10),\n",
- " (10, 10, 10),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 10),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 5, 5),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 10, 10),\n",
- " (10, 10, 10),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 10),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 5, 5),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 10, 10),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 5, 5),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 10, 5),\n",
- " (10, 10, 5),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 10, 1),\n",
- " (10, 5, 5),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 5, 5),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 5, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (10, 1, 1),\n",
- " (5, 5, 1),\n",
- " (5, 5, 1),\n",
- " (5, 5, 1),\n",
- " (5, 5, 1),\n",
- " (5, 5, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (5, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1),\n",
- " (1, 1, 1)]"
- ]
- },
- "execution_count": 33,
- "metadata": {},
- "output_type": "execute_result"
+ "ename": "NameError",
+ "evalue": "name 'it' is not defined",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_53141/4009393498.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcombinations\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbills\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mNameError\u001b[0m: name 'it' is not defined"
+ ]
}
],
"source": [
@@ -2194,7 +1707,6 @@
},
{
"cell_type": "markdown",
- "id": "dependent-royalty",
"metadata": {},
"source": [
"To solve the problem, you can loop over the positive integers from `1` to `len(bills)`, then check which combinations of each size add up to $100:"
@@ -2203,7 +1715,6 @@
{
"cell_type": "code",
"execution_count": 34,
- "id": "cleared-pontiac",
"metadata": {},
"outputs": [],
"source": [
@@ -2216,7 +1727,6 @@
},
{
"cell_type": "markdown",
- "id": "exclusive-vector",
"metadata": {},
"source": [
"If you print out `makes_100`, you will notice there are a lot of repeated combinations. This makes sense because you can make change for `$100` with three `$20` dollar bills and four `$10` bills, but `combinations()` does this with the first four `$10` dollars bills in your wallet; the first, third, fourth and fifth `$10` dollar bills; the first, second, fourth and fifth `$10` bills; and so on."
@@ -2225,7 +1735,6 @@
{
"cell_type": "code",
"execution_count": 35,
- "id": "digital-model",
"metadata": {},
"outputs": [
{
@@ -2249,7 +1758,6 @@
},
{
"cell_type": "markdown",
- "id": "robust-trading",
"metadata": {},
"source": [
"So, there are five ways to make change for a $100 bill with the bills you have in your wallet."
@@ -2257,7 +1765,6 @@
},
{
"cell_type": "markdown",
- "id": "sublime-chick",
"metadata": {},
"source": [
"Here’s a variation on the same problem:\n",
@@ -2267,7 +1774,6 @@
},
{
"cell_type": "markdown",
- "id": "flexible-destiny",
"metadata": {},
"source": [
"In this case, you don’t have a pre-set collection of bills, so you need a way to generate all possible combinations using any number of bills. For this, you’ll need the `itertools.combinations_with_replacement()` function."
@@ -2275,7 +1781,6 @@
},
{
"cell_type": "markdown",
- "id": "documented-trader",
"metadata": {},
"source": [
"It works just like `combinations()`, accepting an iterable inputs and a positive integer `n`, and returns an iterator over n-tuples of elements from inputs. The difference is that `combinations_with_replacement()` allows elements to be repeated in the tuples it returns."
@@ -2283,7 +1788,6 @@
},
{
"cell_type": "markdown",
- "id": "atomic-packaging",
"metadata": {},
"source": [
"For example:"
@@ -2292,7 +1796,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "intelligent-indonesia",
"metadata": {},
"outputs": [
{
@@ -2312,7 +1815,6 @@
},
{
"cell_type": "markdown",
- "id": "optimum-legend",
"metadata": {},
"source": [
"Compare that to `combinations()`:"
@@ -2321,7 +1823,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "equipped-praise",
"metadata": {},
"outputs": [
{
@@ -2341,7 +1842,6 @@
},
{
"cell_type": "markdown",
- "id": "annual-analyst",
"metadata": {},
"source": [
"Here’s what the solution to the revised problem looks like:"
@@ -2350,7 +1850,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "published-bearing",
"metadata": {},
"outputs": [
{
@@ -2374,7 +1873,6 @@
},
{
"cell_type": "markdown",
- "id": "thousand-hampton",
"metadata": {},
"source": [
"In this case, you do not need to remove any duplicates since `combinations_with_replacement()` won’t produce any:"
@@ -2383,7 +1881,6 @@
{
"cell_type": "code",
"execution_count": 46,
- "id": "adequate-trainer",
"metadata": {},
"outputs": [
{
@@ -2403,7 +1900,6 @@
},
{
"cell_type": "markdown",
- "id": "early-printer",
"metadata": {},
"source": [
"If you run the above solution, you may notice that it takes a while for the output to display. That is because it has to process 96,560,645 combinations!"
@@ -2411,7 +1907,6 @@
},
{
"cell_type": "markdown",
- "id": "acute-klein",
"metadata": {},
"source": [
"Another “brute force” `itertools` function is `permutations()`, which accepts a single iterable and produces all possible permutations (rearrangements) of its elements:"
@@ -2420,7 +1915,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "fifteen-laptop",
"metadata": {},
"outputs": [
{
@@ -2445,7 +1939,6 @@
},
{
"cell_type": "markdown",
- "id": "sophisticated-creator",
"metadata": {},
"source": [
"Any iterable of three elements will have six permutations, and the number of permutations of longer iterables grows extremely fast. In fact, an iterable of length `n` has `n!` permutations."
@@ -2453,7 +1946,6 @@
},
{
"cell_type": "markdown",
- "id": "obvious-geneva",
"metadata": {},
"source": [
"The phenomenon of just a few inputs producing a large number of outcomes is called a [combinatorial explosion](https://en.wikipedia.org/wiki/Combinatorial_explosion) and is something to keep in mind when working with `combinations()`, `combinations_with_replacement()`, and `permutations()`."
@@ -2461,7 +1953,6 @@
},
{
"cell_type": "markdown",
- "id": "modified-gossip",
"metadata": {},
"source": [
"It is usually best to avoid brute force algorithms, although there are times you may need to use one (for example, if the correctness of the algorithm is critical, or every possible outcome must be considered). In that case, itertools has you covered."
@@ -2469,15 +1960,14 @@
},
{
"cell_type": "markdown",
- "id": "superior-aircraft",
"metadata": {},
"source": [
- "## Sequences of Numbers"
+ "\n",
+ "## Sequences of Numbers [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "interested-medicine",
"metadata": {},
"source": [
"With `itertools`, you can easily generate iterators over infinite sequences. In this section, you will explore numeric sequences, but the tools and techniques seen here are by no means limited to numbers."
@@ -2485,15 +1975,14 @@
},
{
"cell_type": "markdown",
- "id": "responsible-today",
"metadata": {},
"source": [
- "### Evens and Odds"
+ "\n",
+ "### Evens and Odds [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "professional-usage",
"metadata": {},
"source": [
"For the first example, you will create a pair of iterators over even and odd integers without explicitly doing any arithmetic. Before diving in, let’s look at an arithmetic solution using"
@@ -2501,7 +1990,6 @@
},
{
"cell_type": "markdown",
- "id": "encouraging-mixture",
"metadata": {},
"source": [
"For the first example, you will create a pair of iterators over even and odd integers without explicitly doing any arithmetic. Before diving in, let’s look at an arithmetic solution using generators:"
@@ -2510,7 +1998,6 @@
{
"cell_type": "code",
"execution_count": 41,
- "id": "naval-clause",
"metadata": {},
"outputs": [],
"source": [
@@ -2525,7 +2012,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "accurate-convertible",
"metadata": {},
"outputs": [
{
@@ -2547,7 +2033,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "thrown-poetry",
"metadata": {},
"outputs": [],
"source": [
@@ -2562,7 +2047,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "adequate-milwaukee",
"metadata": {},
"outputs": [
{
@@ -2583,7 +2067,6 @@
},
{
"cell_type": "markdown",
- "id": "biological-payment",
"metadata": {},
"source": [
"That is pretty straightforward, but with `itertools` you can do this much more compactly. The function you need is `itertools.count()`, which does exactly what it sounds like: it counts, starting by default with the number `0`."
@@ -2592,7 +2075,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "leading-explanation",
"metadata": {},
"outputs": [
{
@@ -2613,7 +2095,6 @@
},
{
"cell_type": "markdown",
- "id": "modular-challenge",
"metadata": {},
"source": [
"You can start counting from any number you like by setting the start keyword argument, which defaults to `0`. You can even set a `step` keyword argument to determine the interval between numbers returned from `count()`—this defaults to `1`."
@@ -2621,7 +2102,6 @@
},
{
"cell_type": "markdown",
- "id": "palestinian-politics",
"metadata": {},
"source": [
"With count(), iterators over even and odd integers become literal one-liners:"
@@ -2630,7 +2110,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "nasty-coating",
"metadata": {},
"outputs": [
{
@@ -2652,7 +2131,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "broad-plasma",
"metadata": {},
"outputs": [
{
@@ -2673,7 +2151,6 @@
},
{
"cell_type": "markdown",
- "id": "atmospheric-algeria",
"metadata": {},
"source": [
"Ever since Python 3.1, the `count()` function also accepts non-integer arguments:"
@@ -2682,7 +2159,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "talented-vermont",
"metadata": {},
"outputs": [
{
@@ -2703,7 +2179,6 @@
},
{
"cell_type": "markdown",
- "id": "amino-nomination",
"metadata": {},
"source": [
"You can even pass it negative numbers:"
@@ -2712,7 +2187,6 @@
{
"cell_type": "code",
"execution_count": 164,
- "id": "overall-feature",
"metadata": {},
"outputs": [
{
@@ -2733,7 +2207,6 @@
},
{
"cell_type": "markdown",
- "id": "secondary-pollution",
"metadata": {},
"source": [
"In some ways, `count()` is similar to the built-in `range()` function, but `count()` always returns an infinite sequence. You might wonder what good an infinite sequence is since it’s impossible to iterate over completely. That is a valid question, and I admit the first time I was introduced to infinite iterators, I too didn’t quite see the point."
@@ -2741,7 +2214,6 @@
},
{
"cell_type": "markdown",
- "id": "ignored-surgery",
"metadata": {},
"source": [
"The example that made me realize the power of the infinite iterator was the following, which emulates the behavior of the built-in `enumerate()` function:"
@@ -2750,7 +2222,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "cross-burke",
"metadata": {},
"outputs": [
{
@@ -2770,7 +2241,6 @@
},
{
"cell_type": "markdown",
- "id": "commercial-lafayette",
"metadata": {},
"source": [
"It is a simple example, but think about it: you just enumerated a list without a `for` loop and without knowing the length of the list ahead of time."
@@ -2778,15 +2248,14 @@
},
{
"cell_type": "markdown",
- "id": "employed-journalism",
"metadata": {},
"source": [
- "## Recurrence Relations"
+ "\n",
+ "## Recurrence Relations [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "remarkable-klein",
"metadata": {},
"source": [
"A [recurrence relation](https://en.wikipedia.org/wiki/Recurrence_relation) is a way of describing a sequence of numbers with a recursive formula. One of the best-known recurrence relations is the one that describes the [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number)."
@@ -2794,7 +2263,6 @@
},
{
"cell_type": "markdown",
- "id": "professional-sympathy",
"metadata": {},
"source": [
"The Fibonacci sequence is the sequence 0, 1, 1, 2, 3, 5, 8, 13, .... It starts with 0 and 1, and each subsequent number in the sequence is the sum of the previous two. The numbers in this sequence are called the Fibonacci numbers. In mathematical notation, the recurrence relation describing the n-th Fibonacci number looks like this:\n",
@@ -2804,7 +2272,6 @@
},
{
"cell_type": "markdown",
- "id": "small-numbers",
"metadata": {},
"source": [
"It is common to see the Fibonacci sequence produced with a generator:"
@@ -2813,7 +2280,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "preceding-surgeon",
"metadata": {},
"outputs": [],
"source": [
@@ -2826,7 +2292,6 @@
},
{
"cell_type": "markdown",
- "id": "surgical-indonesia",
"metadata": {},
"source": [
"The recurrence relation describing the Fibonacci numbers is called a second order recurrence relation because, to calculate the next number in the sequence, you need to look back two numbers behind it."
@@ -2834,7 +2299,6 @@
},
{
"cell_type": "markdown",
- "id": "accomplished-tracker",
"metadata": {},
"source": [
"In general, second order recurrence relations have the form:\n",
@@ -2844,7 +2308,6 @@
},
{
"cell_type": "markdown",
- "id": "impaired-telescope",
"metadata": {},
"source": [
"Here, P, Q, and R are constants. To generate the sequence, you need two initial values. For the Fibonacci numbers, `P = Q = 1`, `R = 0`, and the initial values are `0` and `1`."
@@ -2852,7 +2315,6 @@
},
{
"cell_type": "markdown",
- "id": "commercial-australian",
"metadata": {},
"source": [
"As you might guess, a first order recurrence relation has the following form:\n",
@@ -2862,7 +2324,6 @@
},
{
"cell_type": "markdown",
- "id": "complimentary-bronze",
"metadata": {},
"source": [
"There are countless sequences of numbers that can be described by first and second order recurrence relations. For example, the positive integers can be described as a first order recurrence relation with `P = Q = 1` and initial value 1. For the even integers, take `P = 1` and `Q = 2` with initial value 0."
@@ -2870,7 +2331,6 @@
},
{
"cell_type": "markdown",
- "id": "directed-provincial",
"metadata": {},
"source": [
"In this section, you will construct functions for producing any sequence whose values can be described with a first or second order recurrence relation."
@@ -2878,15 +2338,14 @@
},
{
"cell_type": "markdown",
- "id": "premier-fabric",
"metadata": {},
"source": [
- "## First Order Recurrence Relations\n"
+ "\n",
+ "## First Order Recurrence Relations [
](#table_of_contents)\n"
]
},
{
"cell_type": "markdown",
- "id": "separated-checklist",
"metadata": {},
"source": [
"You’ve already seen how `count()` can generate the sequence of non-negative integers, the even integers, and the odd integers. You can also use it to generate the sequence `3n = 0, 3, 6, 9, 12, …` and `4n = 0, 4, 8, 12, 16, …`."
@@ -2895,7 +2354,6 @@
{
"cell_type": "code",
"execution_count": 175,
- "id": "herbal-destiny",
"metadata": {},
"outputs": [],
"source": [
@@ -2905,7 +2363,6 @@
},
{
"cell_type": "markdown",
- "id": "compressed-queens",
"metadata": {},
"source": [
"In fact, `count()` can produce sequences of multiples of any number you wish. These sequences can be described with first-order recurrence relations. For example, to generate the sequence of multiples of some number `n`, just take `P = 1, Q = n`, and initial value 0."
@@ -2913,7 +2370,6 @@
},
{
"cell_type": "markdown",
- "id": "ethical-vertex",
"metadata": {},
"source": [
"Another easy example of a first-order recurrence relation is the constant sequence `n, n, n, n, n, …`, where n is any value you’d like. For this sequence, set `P = 1` and `Q = 0` with initial value n. itertools provides an easy way to implement this sequence as well, with the `repeat()` function:"
@@ -2922,7 +2378,6 @@
{
"cell_type": "code",
"execution_count": 185,
- "id": "accurate-vessel",
"metadata": {},
"outputs": [],
"source": [
@@ -2932,7 +2387,6 @@
},
{
"cell_type": "markdown",
- "id": "amber-curtis",
"metadata": {},
"source": [
"If you need a finite sequence of repeated values, you can set a stopping point by passing a positive integer as a second argument:"
@@ -2941,7 +2395,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "leading-hunter",
"metadata": {},
"outputs": [],
"source": [
@@ -2951,7 +2404,6 @@
},
{
"cell_type": "markdown",
- "id": "indoor-affect",
"metadata": {},
"source": [
"What may not be quite as obvious is that the sequence `1, -1, 1, -1, 1, -1, ...` of alternating 1s and -1s can also be described by a first order recurrence relation. Just take `P = -1`, `Q = 0`, and initial value `1`."
@@ -2959,7 +2411,6 @@
},
{
"cell_type": "markdown",
- "id": "awful-function",
"metadata": {},
"source": [
"There’s an easy way to generate this sequence with the `itertools.cycle()` function. This function takes an iterable inputs as an argument and returns an infinite iterator over the values in inputs that returns to the beginning once the end of inputs is reached. So, to produce the alternating sequence of 1s and -1s, you could do this:"
@@ -2968,7 +2419,6 @@
{
"cell_type": "code",
"execution_count": 232,
- "id": "studied-tribune",
"metadata": {},
"outputs": [],
"source": [
@@ -2977,7 +2427,6 @@
},
{
"cell_type": "markdown",
- "id": "economic-melissa",
"metadata": {},
"source": [
"The goal of this section, though, is to produce a single function that can generate any first order recurrence relation—just pass it `P`, `Q`, and an initial value. One way to do this is with `itertools.accumulate()`."
@@ -2985,7 +2434,6 @@
},
{
"cell_type": "markdown",
- "id": "flying-interpretation",
"metadata": {},
"source": [
"The `accumulate()` function takes two arguments—an iterable `inputs` and a **binary function** `func` (that is, a function with exactly two inputs)—and returns an iterator over accumulated results of applying func to elements of inputs. It is roughly equivalent to the following generator:"
@@ -2994,7 +2442,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "recent-lancaster",
"metadata": {},
"outputs": [],
"source": [
@@ -3009,7 +2456,6 @@
{
"cell_type": "code",
"execution_count": 32,
- "id": "proper-inspector",
"metadata": {},
"outputs": [
{
@@ -3030,7 +2476,6 @@
},
{
"cell_type": "markdown",
- "id": "covered-bailey",
"metadata": {},
"source": [
"The first value in the iterator returned by `accumulate()` is always the first value in the input sequence. In the above example, this is 1—the first value in `[1, 2, 3, 4, 5]`."
@@ -3038,7 +2483,6 @@
},
{
"cell_type": "markdown",
- "id": "bronze-salad",
"metadata": {},
"source": [
"The next value in the output iterator is the sum of the first two elements of the input sequence: `add(1, 2) = 3`. To produce the next value, `accumulate()` takes the result of `add(1, 2)` and adds this to the third value in the input sequence:\n",
@@ -3050,7 +2494,6 @@
},
{
"cell_type": "markdown",
- "id": "known-november",
"metadata": {},
"source": [
"The fourth value produced by `accumulate()` is `add(add(add(1, 2), 3), 4) = 10`, and so on."
@@ -3058,7 +2501,6 @@
},
{
"cell_type": "markdown",
- "id": "unusual-maker",
"metadata": {},
"source": [
"The second argument of `accumulate()` defaults to `operator.add()`, so the previous example can be simplified to:"
@@ -3067,7 +2509,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "racial-belle",
"metadata": {},
"outputs": [
{
@@ -3087,7 +2528,6 @@
},
{
"cell_type": "markdown",
- "id": "elegant-michael",
"metadata": {},
"source": [
"Passing the built-in `min()` to `accumulate()` will keep track of a running minimum:"
@@ -3096,7 +2536,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "accessible-beast",
"metadata": {},
"outputs": [
{
@@ -3116,7 +2555,6 @@
},
{
"cell_type": "markdown",
- "id": "separated-bread",
"metadata": {},
"source": [
"More complex functions can be passed to `accumulate()` with lambda expressions:"
@@ -3125,7 +2563,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "cardiovascular-reservoir",
"metadata": {},
"outputs": [
{
@@ -3145,7 +2582,6 @@
},
{
"cell_type": "markdown",
- "id": "electronic-salmon",
"metadata": {},
"source": [
"The order of the arguments in the binary function passed to `accumulate()` is important. The first argument is always the previously accumulated result and the second argument is always the next element of the input iterable. For example, consider the difference in output of the following expressions:"
@@ -3154,7 +2590,6 @@
{
"cell_type": "code",
"execution_count": 45,
- "id": "material-category",
"metadata": {},
"outputs": [
{
@@ -3175,7 +2610,6 @@
{
"cell_type": "code",
"execution_count": 46,
- "id": "balanced-spider",
"metadata": {},
"outputs": [
{
@@ -3195,7 +2629,6 @@
},
{
"cell_type": "markdown",
- "id": "informed-dealing",
"metadata": {},
"source": [
"To model a recurrence relation, you can just ignore the second argument of the binary function passed to `accumulate()`. That is, given values `p`, `q`, and `s`, `lambda x, _: p*s + q` will return the value following `x` in the recurrence relation defined by $s_i = Ps_{i-1} + Q$."
@@ -3203,7 +2636,6 @@
},
{
"cell_type": "markdown",
- "id": "interesting-extraction",
"metadata": {},
"source": [
"In order for `accumulate()` to iterate over the resulting recurrence relation, you need to pass to it an infinite sequence with the right initial value. It doesn’t matter what the rest of the values in the sequence are, as long as the initial value is the initial value of the recurrence relation. You can do this is with `repeat()`:"
@@ -3212,7 +2644,6 @@
{
"cell_type": "code",
"execution_count": 47,
- "id": "moved-capacity",
"metadata": {},
"outputs": [],
"source": [
@@ -3223,7 +2654,6 @@
},
{
"cell_type": "markdown",
- "id": "opponent-progress",
"metadata": {},
"source": [
"Using `first_order()`, you can build the sequences from above as follows:"
@@ -3232,7 +2662,6 @@
{
"cell_type": "code",
"execution_count": 49,
- "id": "little-vienna",
"metadata": {},
"outputs": [
{
@@ -3254,7 +2683,6 @@
{
"cell_type": "code",
"execution_count": 50,
- "id": "boring-holocaust",
"metadata": {},
"outputs": [
{
@@ -3276,7 +2704,6 @@
{
"cell_type": "code",
"execution_count": 51,
- "id": "indie-criticism",
"metadata": {},
"outputs": [
{
@@ -3298,7 +2725,6 @@
{
"cell_type": "code",
"execution_count": 52,
- "id": "plastic-future",
"metadata": {},
"outputs": [
{
@@ -3320,7 +2746,6 @@
{
"cell_type": "code",
"execution_count": 53,
- "id": "alpha-palace",
"metadata": {},
"outputs": [
{
@@ -3342,7 +2767,6 @@
{
"cell_type": "code",
"execution_count": 54,
- "id": "rational-foundation",
"metadata": {},
"outputs": [
{
@@ -3364,7 +2788,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "blank-closing",
"metadata": {},
"outputs": [
{
@@ -3385,15 +2808,14 @@
},
{
"cell_type": "markdown",
- "id": "extended-optimization",
"metadata": {},
"source": [
- "## Second Order Recurrence Relations"
+ "\n",
+ "## Second Order Recurrence Relations [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "guilty-fiction",
"metadata": {},
"source": [
"Generating sequences described by second order recurrence relations, like the Fibonacci sequence, can be accomplished using a similar technique as the one used for first order recurrence relations."
@@ -3401,7 +2823,6 @@
},
{
"cell_type": "markdown",
- "id": "neither-effectiveness",
"metadata": {},
"source": [
"The difference here is that you need to create an intermediate sequence of tuples that keep track of the previous two elements of the sequence, and then map() each of these tuples to their first component to get the final sequence."
@@ -3409,7 +2830,6 @@
},
{
"cell_type": "markdown",
- "id": "civilian-tutorial",
"metadata": {},
"source": [
"Here’s what it looks like:"
@@ -3417,8 +2837,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
- "id": "central-helmet",
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -3433,7 +2852,6 @@
},
{
"cell_type": "markdown",
- "id": "copyrighted-pulse",
"metadata": {},
"source": [
"Using `second_order()`, you can generate the Fibonacci sequence like this:"
@@ -3441,8 +2859,7 @@
},
{
"cell_type": "code",
- "execution_count": 58,
- "id": "floating-produce",
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
@@ -3451,7 +2868,7 @@
"[0, 1, 1, 2, 3, 5, 8, 13]"
]
},
- "execution_count": 58,
+ "execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
@@ -3463,7 +2880,6 @@
},
{
"cell_type": "markdown",
- "id": "funny-clearance",
"metadata": {},
"source": [
"Other sequences can be easily generated by changing the values of `p`, `q`, and `r`. For example, the [Pell numbers](https://en.wikipedia.org/wiki/Pell_number) and the [Lucas numbers](https://en.wikipedia.org/wiki/Lucas_number) can be generated as follows:"
@@ -3472,7 +2888,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "guided-gamma",
"metadata": {},
"outputs": [
{
@@ -3494,7 +2909,6 @@
{
"cell_type": "code",
"execution_count": 60,
- "id": "democratic-rugby",
"metadata": {},
"outputs": [
{
@@ -3515,7 +2929,6 @@
},
{
"cell_type": "markdown",
- "id": "dynamic-standing",
"metadata": {},
"source": [
"You can even generate the alternating Fibonacci numbers:"
@@ -3524,7 +2937,6 @@
{
"cell_type": "code",
"execution_count": 61,
- "id": "conceptual-belly",
"metadata": {},
"outputs": [
{
@@ -3545,7 +2957,6 @@
},
{
"cell_type": "markdown",
- "id": "reserved-collector",
"metadata": {},
"source": [
"This is a valuable lesson. The accumulate() function is a powerful tool to have in your toolkit, but there are times when using it could mean sacrificing clarity and readability."
@@ -3568,7 +2979,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.10"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/12. Descriptors.ipynb b/Python/04. Advanced/05.1 Descriptors.ipynb
similarity index 93%
rename from python/04. Advanced/12. Descriptors.ipynb
rename to Python/04. Advanced/05.1 Descriptors.ipynb
index 914ad23..bebee0a 100755
--- a/python/04. Advanced/12. Descriptors.ipynb
+++ b/Python/04. Advanced/05.1 Descriptors.ipynb
@@ -2,20 +2,55 @@
"cells": [
{
"cell_type": "markdown",
- "id": "strong-tracy",
"metadata": {},
"source": [
- "# Descriptors\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Descriptors \n",
"\n",
"Descriptors are a specific Python feature that power a lot of the magic hidden under the language’s hood. If you’ve ever thought that Python descriptors are an advanced topic with few practical applications, then this tutorial is the perfect tool to help you understand this powerful feature. You’ll come to understand why Python descriptors are such an interesting topic, and what kind of use cases you can apply them to."
]
},
{
"cell_type": "markdown",
- "id": "contemporary-shelter",
"metadata": {},
"source": [
- "## What Are Python Descriptors?\n",
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [What Are Python Descriptors?](#what_are_python_descriptors?)\n",
+ "* [Purpose of Descriptors](#purpose_of_descriptors)\n",
+ " * [Dynamic Lookup](#dynamic_lookup)\n",
+ " * [Managed attributes](#managed_attributes)\n",
+ " * [Customized names](#customized_names)\n",
+ " * [Closing thoughts](#closing_thoughts)\n",
+ "* [Complete Practical Example](#complete_practical_example)\n",
+ " * [Validator class](#validator_class)\n",
+ " * [Custom validators](#custom_validators)\n",
+ " * [Practical application](#practical_application)\n",
+ " * [Pure Python Equivalents](#pure_python_equivalents)\n",
+ "* [Python Descriptors in Methods and Functions](#python_descriptors_in_methods_and_functions)\n",
+ "* [How Attributes Are Accessed With the Lookup Chain](#how_attributes_are_accessed_with_the_lookup_chain)\n",
+ "* [Why Use Python Descriptors?](#why_use_python_descriptors?)\n",
+ " * [Lazy Properties](#lazy_properties)\n",
+ " * [D.R.Y. Code](#d.r.y._code)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## What Are Python Descriptors? [
](#table_of_contents)\n",
"\n",
"Descriptors are Python objects that implement a method of the descriptor protocol, which gives you the ability to create objects that have special behavior when they’re accessed as attributes of other objects. They are a general-purpose way of intercepting attribute access. Descriptors are the mechanism behind properties' static methods, class methods, super methods, etc.\n",
"\n",
@@ -32,7 +67,6 @@
},
{
"cell_type": "markdown",
- "id": "differential-ballot",
"metadata": {},
"source": [
"If your descriptor implements just `.__get__()`, then it’s said to be a **non-data descriptor**. If it implements `.__set__()` or `.__delete__()`, then it’s said to be a **data descriptor**. Note that this difference is not just about the name, but it’s also a difference in behavior. The non-data descriptors are only readable while the data descriptors are both readable and writable. That’s because data descriptors have precedence during the lookup process, as you’ll see later on."
@@ -40,7 +74,6 @@
},
{
"cell_type": "markdown",
- "id": "headed-chest",
"metadata": {},
"source": [
"It is important to note that descriptors are assigned to a class, not to the instance of a class. Modifying the class overwrites or deletes the descriptor itself, rather than triggering its code.\n",
@@ -50,7 +83,6 @@
},
{
"cell_type": "markdown",
- "id": "portable-opposition",
"metadata": {},
"source": [
"Let's understand the get, set, and delete methods in more detail inspired by this IBM Developer page:\n",
@@ -68,15 +100,14 @@
},
{
"cell_type": "markdown",
- "id": "unusual-invasion",
"metadata": {},
"source": [
- "## Purpose of Descriptors"
+ "\n",
+ "## Purpose of Descriptors [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "adaptive-camel",
"metadata": {},
"source": [
"Let's define a class car that has three attributes, namely `brand` and `fuel_cap`. You will use the `__init__()` method to initialize the attributes of the class. Then, you will use the magic function `__str__()`, which will simply return the output of the three attributes that you will pass to the class while creating the object."
@@ -85,7 +116,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "seeing-syndrome",
"metadata": {},
"outputs": [],
"source": [
@@ -101,7 +131,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "german-repair",
"metadata": {},
"outputs": [
{
@@ -119,7 +148,6 @@
},
{
"cell_type": "markdown",
- "id": "close-repeat",
"metadata": {},
"source": [
"So as you can see from the above output, everything looks great!"
@@ -127,7 +155,6 @@
},
{
"cell_type": "markdown",
- "id": "ordered-party",
"metadata": {},
"source": [
"Now let's change the fuel capacity of the car to negative 40."
@@ -136,7 +163,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "front-atlanta",
"metadata": {},
"outputs": [
{
@@ -154,7 +180,6 @@
},
{
"cell_type": "markdown",
- "id": "compatible-pantyhose",
"metadata": {},
"source": [
"Hold on, there is something wrong, isn't it? The fuel capacity of the car can never be negative. However, Python accepts it as an input without any error. That's because Python is a dynamic programming language that does not support type-checking explicitly."
@@ -162,7 +187,6 @@
},
{
"cell_type": "markdown",
- "id": "featured-embassy",
"metadata": {},
"source": [
"To avoid this issue, let's add an `if` condition in the `__init__()` method and check whether the inputted fuel capacity is valid or invalid. If the fuel capacity entered is invalid, then raise a ValueError exception."
@@ -171,7 +195,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "duplicate-challenge",
"metadata": {},
"outputs": [],
"source": [
@@ -190,7 +213,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "center-trinidad",
"metadata": {},
"outputs": [
{
@@ -213,7 +235,6 @@
},
{
"cell_type": "markdown",
- "id": "mounted-button",
"metadata": {},
"source": [
"From the above output, you can observe that everything works fine for now since the program raises a `ValueError` if the fuel capacity is below zero."
@@ -221,7 +242,6 @@
},
{
"cell_type": "markdown",
- "id": "paperback-completion",
"metadata": {},
"source": [
"However, what if you would like to change the fuel capacity attribute to negative 40 explicitly later on. In this case, it will not work, since the type-checking will be done only in the `__init__()` method once. As you would know, the `__init__()` method is a constructor, and it is called only once when you create an object of the class. Hence, the custom type-checking will fail later on."
@@ -229,7 +249,6 @@
},
{
"cell_type": "markdown",
- "id": "phantom-cliff",
"metadata": {},
"source": [
"Let's understand it with an example."
@@ -238,7 +257,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "eastern-stage",
"metadata": {},
"outputs": [],
"source": [
@@ -257,7 +275,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "metric-binding",
"metadata": {},
"outputs": [
{
@@ -276,7 +293,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "norwegian-portfolio",
"metadata": {},
"outputs": [],
"source": [
@@ -285,7 +301,6 @@
},
{
"cell_type": "markdown",
- "id": "powered-bolivia",
"metadata": {},
"source": [
"And there you go! You were able to break out of type-checking."
@@ -293,7 +308,6 @@
},
{
"cell_type": "markdown",
- "id": "quiet-client",
"metadata": {},
"source": [
"Now think it this way, what if you have many other attributes of the car like `mileage`, `price`, `accessories`, etc. which requires type-checking as well and you also would like functionality in which some of these attributes have only `read` access. Wouldn't that be so annoying?"
@@ -301,7 +315,6 @@
},
{
"cell_type": "markdown",
- "id": "competent-privacy",
"metadata": {},
"source": [
"Well, to address all of the above problems, Python has Descriptors that come to the rescue!"
@@ -309,7 +322,6 @@
},
{
"cell_type": "markdown",
- "id": "stainless-nickel",
"metadata": {},
"source": [
"As you learned above that any class that implements `__get__()`, `__set()__`, or `__delete()__` magic methods for an object of descriptor protocol are called Descriptors. They also give you additional control over how an attribute should work like whether it should have a read or write access."
@@ -317,7 +329,6 @@
},
{
"cell_type": "markdown",
- "id": "determined-influence",
"metadata": {},
"source": [
"Now let's extend the above example by adding the Python Descriptor methods."
@@ -326,7 +337,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "correct-jordan",
"metadata": {},
"outputs": [],
"source": [
@@ -356,7 +366,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "former-gospel",
"metadata": {},
"outputs": [],
"source": [
@@ -374,7 +383,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "extended-merchant",
"metadata": {},
"outputs": [
{
@@ -392,7 +400,6 @@
{
"cell_type": "code",
"execution_count": 95,
- "id": "resident-horror",
"metadata": {},
"outputs": [
{
@@ -411,7 +418,6 @@
},
{
"cell_type": "markdown",
- "id": "every-theology",
"metadata": {},
"source": [
"Perfect! So as you can see, it works when you update the fuel capacity attribute later on.\n",
@@ -422,7 +428,6 @@
{
"cell_type": "code",
"execution_count": 76,
- "id": "handled-yield",
"metadata": {},
"outputs": [
{
@@ -450,7 +455,6 @@
{
"cell_type": "code",
"execution_count": 77,
- "id": "talented-cemetery",
"metadata": {},
"outputs": [
{
@@ -468,7 +472,6 @@
{
"cell_type": "code",
"execution_count": 78,
- "id": "activated-spectrum",
"metadata": {},
"outputs": [
{
@@ -495,15 +498,14 @@
},
{
"cell_type": "markdown",
- "id": "later-stability",
"metadata": {},
"source": [
- "### Dynamic Lookup"
+ "\n",
+ "### Dynamic Lookup [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "current-determination",
"metadata": {},
"source": [
"Interesting descriptors typically run computations instead of returning constants:"
@@ -512,7 +514,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "pediatric-crossing",
"metadata": {},
"outputs": [],
"source": [
@@ -534,7 +535,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "australian-spirit",
"metadata": {},
"outputs": [],
"source": [
@@ -544,7 +544,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "exclusive-modern",
"metadata": {},
"outputs": [
{
@@ -565,7 +564,6 @@
{
"cell_type": "code",
"execution_count": 13,
- "id": "stunning-privilege",
"metadata": {},
"outputs": [],
"source": [
@@ -575,7 +573,6 @@
{
"cell_type": "code",
"execution_count": 14,
- "id": "growing-cooking",
"metadata": {},
"outputs": [
{
@@ -595,7 +592,6 @@
},
{
"cell_type": "markdown",
- "id": "coral-japan",
"metadata": {},
"source": [
"Besides showing how descriptors can run computations, this example also reveals the purpose of the parameters to `__get__()`. The self parameter is size, an instance of DirectorySize. The obj parameter is either g or s, an instance of Directory. It is the obj parameter that lets the `__get__()` method learn the target directory. The objtype parameter is the class Directory."
@@ -603,15 +599,14 @@
},
{
"cell_type": "markdown",
- "id": "returning-postage",
"metadata": {},
"source": [
- "### Managed attributes"
+ "\n",
+ "### Managed attributes [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "forbidden-testimony",
"metadata": {},
"source": [
"A popular use for descriptors is managing access to instance data. The descriptor is assigned to a public attribute in the class dictionary while the actual data is stored as a private attribute in the instance dictionary. The descriptor’s `__get__()` and `__set__()` methods are triggered when the public attribute is accessed."
@@ -619,7 +614,6 @@
},
{
"cell_type": "markdown",
- "id": "spectacular-eleven",
"metadata": {},
"source": [
"In the following example, `age` is the public attribute and `_age` is the private attribute. When the public attribute is accessed, the descriptor logs the lookup or update:"
@@ -627,8 +621,7 @@
},
{
"cell_type": "code",
- "execution_count": 141,
- "id": "jewish-viking",
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -650,6 +643,7 @@
"class Person:\n",
"\n",
" age = LoggedAgeAccess() # Descriptor instance\n",
+ " name = LoggedAgeAccess()\n",
"\n",
" def __init__(self, name, age):\n",
" self.name = name # Regular instance attribute\n",
@@ -661,16 +655,14 @@
},
{
"cell_type": "markdown",
- "id": "dominant-level",
"metadata": {},
"source": [
- "An interactive session shows that all access to the managed attribute age is logged, but that the regular attribute name is not logged:m"
+ "An interactive session shows that all access to the managed attribute age is logged, but that the regular attribute name is not logged:"
]
},
{
"cell_type": "code",
- "execution_count": 142,
- "id": "regulated-course",
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
@@ -687,8 +679,7 @@
},
{
"cell_type": "code",
- "execution_count": 143,
- "id": "congressional-sender",
+ "execution_count": 3,
"metadata": {},
"outputs": [
{
@@ -697,7 +688,7 @@
"{'name': 'Ali', '_age': 28}"
]
},
- "execution_count": 143,
+ "execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@@ -708,8 +699,7 @@
},
{
"cell_type": "code",
- "execution_count": 144,
- "id": "measured-steering",
+ "execution_count": 4,
"metadata": {},
"outputs": [
{
@@ -726,8 +716,7 @@
},
{
"cell_type": "code",
- "execution_count": 145,
- "id": "entire-object",
+ "execution_count": 5,
"metadata": {},
"outputs": [
{
@@ -736,7 +725,7 @@
"'Ali'"
]
},
- "execution_count": 145,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@@ -747,8 +736,7 @@
},
{
"cell_type": "code",
- "execution_count": 146,
- "id": "expensive-cardiff",
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
@@ -764,7 +752,7 @@
"29"
]
},
- "execution_count": 146,
+ "execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@@ -775,7 +763,6 @@
},
{
"cell_type": "markdown",
- "id": "powerful-value",
"metadata": {},
"source": [
"One major issue with this example is that the private name `_age` is hardwired in the `LoggedAgeAccess` class. That means that each instance can only have one logged attribute and that its name is unchangeable. In the next example, we’ll fix that problem."
@@ -783,15 +770,14 @@
},
{
"cell_type": "markdown",
- "id": "referenced-plaintiff",
"metadata": {},
"source": [
- "### Customized names"
+ "\n",
+ "### Customized names [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "possible-airplane",
"metadata": {},
"source": [
"When a class uses descriptors, it can inform each descriptor about which variable name was used."
@@ -799,7 +785,6 @@
},
{
"cell_type": "markdown",
- "id": "injured-platinum",
"metadata": {},
"source": [
"In this example, the `Person` class has two descriptor instances, name and age. When the `Person` class is defined, it makes a callback to `__set_name__()` in `LoggedAccess` so that the field names can be recorded, giving each descriptor its own public name and private name:"
@@ -808,7 +793,6 @@
{
"cell_type": "code",
"execution_count": 166,
- "id": "gorgeous-processing",
"metadata": {},
"outputs": [
{
@@ -856,7 +840,6 @@
{
"cell_type": "code",
"execution_count": 163,
- "id": "agreed-philippines",
"metadata": {},
"outputs": [
{
@@ -875,7 +858,6 @@
{
"cell_type": "code",
"execution_count": 164,
- "id": "miniature-israeli",
"metadata": {},
"outputs": [
{
@@ -903,7 +885,6 @@
{
"cell_type": "code",
"execution_count": 167,
- "id": "dressed-accent",
"metadata": {},
"outputs": [
{
@@ -923,10 +904,10 @@
},
{
"cell_type": "markdown",
- "id": "technical-following",
"metadata": {},
"source": [
- "### Closing thoughts\n",
+ "\n",
+ "### Closing thoughts [
](#table_of_contents)\n",
"A descriptor is what we call any object that defines `__get__()`, `__set__()`, or `__delete__()`.\n",
"\n",
"- Optionally, descriptors can have a `__set_name__()` method. This is only used in cases where a descriptor needs to know either the class where it was created or the name of class variable it was assigned to. (This method, if present, is called even if the class is not a descriptor.)\n",
@@ -944,15 +925,14 @@
},
{
"cell_type": "markdown",
- "id": "floating-shirt",
"metadata": {},
"source": [
- "## Complete Practical Example"
+ "\n",
+ "## Complete Practical Example [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "indie-louisville",
"metadata": {},
"source": [
"In this example, we create a practical and powerful tool for locating notoriously hard to find data corruption bugs."
@@ -960,17 +940,16 @@
},
{
"cell_type": "markdown",
- "id": "stone-stock",
"metadata": {},
"source": [
- "### Validator class\n",
+ "\n",
+ "### Validator class [
](#table_of_contents)\n",
"\n",
"A validator is a descriptor for managed attribute access. Prior to storing any data, it verifies that the new value meets various type and range restrictions. If those restrictions aren’t met, it raises an exception to prevent data corruption at its source."
]
},
{
"cell_type": "markdown",
- "id": "spoken-tooth",
"metadata": {},
"source": [
"This `Validator` class is both an abstract base class and a managed attribute descriptor:"
@@ -979,7 +958,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "national-craft",
"metadata": {},
"outputs": [],
"source": [
@@ -1004,7 +982,6 @@
},
{
"cell_type": "markdown",
- "id": "specific-botswana",
"metadata": {},
"source": [
"Custom validators need to inherit from `Validator` and must supply a `validate()` method to test various restrictions as needed."
@@ -1012,10 +989,10 @@
},
{
"cell_type": "markdown",
- "id": "multiple-request",
"metadata": {},
"source": [
- "### Custom validators\n",
+ "\n",
+ "### Custom validators [
](#table_of_contents)\n",
"Here are three practical data validation utilities:\n",
"\n",
"- `OneOf` verifies that a value is one of a restricted set of options.\n",
@@ -1028,7 +1005,6 @@
{
"cell_type": "code",
"execution_count": 20,
- "id": "seventh-vampire",
"metadata": {},
"outputs": [],
"source": [
@@ -1045,7 +1021,6 @@
{
"cell_type": "code",
"execution_count": 21,
- "id": "parental-republic",
"metadata": {},
"outputs": [],
"source": [
@@ -1071,7 +1046,6 @@
{
"cell_type": "code",
"execution_count": 22,
- "id": "detailed-target",
"metadata": {},
"outputs": [],
"source": [
@@ -1101,15 +1075,14 @@
},
{
"cell_type": "markdown",
- "id": "facial-vector",
"metadata": {},
"source": [
- "### Practical application"
+ "\n",
+ "### Practical application [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "satisfactory-drinking",
"metadata": {},
"source": [
"Here’s how the data validators can be used in a real class:"
@@ -1118,7 +1091,6 @@
{
"cell_type": "code",
"execution_count": 38,
- "id": "earlier-wilson",
"metadata": {},
"outputs": [],
"source": [
@@ -1136,7 +1108,6 @@
},
{
"cell_type": "markdown",
- "id": "british-terrorism",
"metadata": {},
"source": [
"The descriptors prevent invalid instances from being created:"
@@ -1145,7 +1116,6 @@
{
"cell_type": "code",
"execution_count": 39,
- "id": "supposed-sterling",
"metadata": {},
"outputs": [
{
@@ -1171,7 +1141,6 @@
{
"cell_type": "code",
"execution_count": 40,
- "id": "suffering-theme",
"metadata": {},
"outputs": [
{
@@ -1197,7 +1166,6 @@
{
"cell_type": "code",
"execution_count": 41,
- "id": "verbal-flour",
"metadata": {},
"outputs": [
{
@@ -1223,7 +1191,6 @@
{
"cell_type": "code",
"execution_count": 42,
- "id": "sticky-analysis",
"metadata": {},
"outputs": [],
"source": [
@@ -1233,17 +1200,16 @@
},
{
"cell_type": "markdown",
- "id": "ignored-kansas",
"metadata": {},
"source": [
- "### Pure Python Equivalents\n",
+ "\n",
+ "### Pure Python Equivalents [
](#table_of_contents)\n",
"\n",
"The descriptor protocol is simple and offers exciting possibilities. Several use cases are so common that they have been prepackaged into built-in tools. Properties, bound methods, static methods, class methods, and __slots__ are all based on the descriptor protocol."
]
},
{
"cell_type": "markdown",
- "id": "packed-breed",
"metadata": {},
"source": [
"Calling `property()` is a succinct way of building a data descriptor that triggers a function call upon access to an attribute. Its signature is:\n",
@@ -1255,7 +1221,6 @@
},
{
"cell_type": "markdown",
- "id": "timely-ordinary",
"metadata": {},
"source": [
"The documentation shows a typical use to define a managed attribute `x`:"
@@ -1264,7 +1229,6 @@
{
"cell_type": "code",
"execution_count": 182,
- "id": "international-round",
"metadata": {},
"outputs": [],
"source": [
@@ -1277,7 +1241,6 @@
},
{
"cell_type": "markdown",
- "id": "located-handbook",
"metadata": {},
"source": [
"To see how `property()` is implemented in terms of the descriptor protocol, here is a pure Python equivalent:"
@@ -1286,7 +1249,6 @@
{
"cell_type": "code",
"execution_count": 70,
- "id": "bridal-parts",
"metadata": {},
"outputs": [],
"source": [
@@ -1330,7 +1292,6 @@
},
{
"cell_type": "markdown",
- "id": "spanish-cambridge",
"metadata": {},
"source": [
"The following example uses a property that logs a message to the console when it’s accessed:"
@@ -1339,7 +1300,6 @@
{
"cell_type": "code",
"execution_count": 101,
- "id": "tribal-romance",
"metadata": {},
"outputs": [],
"source": [
@@ -1366,7 +1326,6 @@
{
"cell_type": "code",
"execution_count": 203,
- "id": "double-tackle",
"metadata": {},
"outputs": [
{
@@ -1394,7 +1353,6 @@
},
{
"cell_type": "markdown",
- "id": "adapted-pregnancy",
"metadata": {},
"source": [
"The example above makes use of decorators to define a property, but as you may know, decorators are just syntactic sugar. The example before, in fact, can be written as follows:"
@@ -1403,7 +1361,6 @@
{
"cell_type": "code",
"execution_count": 100,
- "id": "hourly-reward",
"metadata": {},
"outputs": [],
"source": [
@@ -1411,7 +1368,7 @@
"class Foo():\n",
" def __init__(self, value):\n",
" self._value = value\n",
- " \n",
+ "\n",
" def getter(self):\n",
" print(\"accessing the attribute to get the value\")\n",
" return self._value\n",
@@ -1430,7 +1387,6 @@
{
"cell_type": "code",
"execution_count": 211,
- "id": "crucial-semiconductor",
"metadata": {},
"outputs": [
{
@@ -1459,7 +1415,6 @@
{
"cell_type": "code",
"execution_count": 212,
- "id": "brown-spouse",
"metadata": {},
"outputs": [
{
@@ -1476,10 +1431,10 @@
},
{
"cell_type": "markdown",
- "id": "loving-briefing",
"metadata": {},
"source": [
- "## Python Descriptors in Methods and Functions\n",
+ "\n",
+ "## Python Descriptors in Methods and Functions [
](#table_of_contents)\n",
"If you’ve ever written an object-oriented program in Python, then you’ve certainly used methods. These are regular functions that have the first argument reserved for the object instance. When you access a method using dot notation, you’re calling the corresponding function and passing the object instance as the first parameter.\n",
"\n",
"The magic that transforms your `obj.method(*args)` call into `method(obj, *args)` is inside a `.__get__()` implementation of the `function` object that is, in fact, a **non-data descriptor**. In particular, the `function` object implements `.__get__()` so that it returns a bound method when you access it with dot notation. The (`*args`) that follow invoke the functions by passing all the extra arguments needed.\n",
@@ -1490,7 +1445,6 @@
{
"cell_type": "code",
"execution_count": 213,
- "id": "military-photography",
"metadata": {},
"outputs": [],
"source": [
@@ -1505,20 +1459,8 @@
" return types.MethodType(self, obj)"
]
},
- {
- "cell_type": "code",
- "execution_count": 102,
- "id": "regular-defensive",
- "metadata": {},
- "outputs": [],
- "source": [
- "def add(a, b):\n",
- " return a + b"
- ]
- },
{
"cell_type": "markdown",
- "id": "supreme-outside",
"metadata": {},
"source": [
"In the example above, when the function is accessed with dot notation, `.__get__()` is called and a bound method is returned."
@@ -1526,7 +1468,6 @@
},
{
"cell_type": "markdown",
- "id": "reserved-gateway",
"metadata": {},
"source": [
"This works for regular instance methods just like it does for class methods or static methods. So, if you call a static method with `obj.method(*args)`, then it’s automatically transformed into `method(*args)`. Similarly, if you call a class method with `obj.method(type(obj), *args)`, then it’s automatically transformed into `method(type(obj), *args)`."
@@ -1534,7 +1475,6 @@
},
{
"cell_type": "markdown",
- "id": "overall-singing",
"metadata": {},
"source": [
"In the official docs, you can find some examples of how static methods and class methods would be implemented if they were written in pure Python instead of the actual C implementation. For instance, a possible static method implementation could be this:"
@@ -1543,7 +1483,6 @@
{
"cell_type": "code",
"execution_count": 214,
- "id": "comic-camcorder",
"metadata": {},
"outputs": [],
"source": [
@@ -1558,7 +1497,6 @@
},
{
"cell_type": "markdown",
- "id": "minimal-nutrition",
"metadata": {},
"source": [
"Likewise, this could be a possible class method implementation:"
@@ -1567,7 +1505,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "meaning-myanmar",
"metadata": {},
"outputs": [],
"source": [
@@ -1581,7 +1518,6 @@
{
"cell_type": "code",
"execution_count": 216,
- "id": "bigger-interview",
"metadata": {},
"outputs": [],
"source": [
@@ -1600,7 +1536,6 @@
},
{
"cell_type": "markdown",
- "id": "musical-algorithm",
"metadata": {},
"source": [
"Note that, in Python, a class method is just a static method that takes the class reference as the first argument of the argument list."
@@ -1608,18 +1543,17 @@
},
{
"cell_type": "markdown",
- "id": "anonymous-mapping",
"metadata": {},
"source": [
- "## How Attributes Are Accessed With the Lookup Chain\n",
+ "\n",
+ "## How Attributes Are Accessed With the Lookup Chain [
](#table_of_contents)\n",
"\n",
"To understand a little more about Python descriptors and Python internals, you need to understand what happens in Python when an attribute is accessed. In Python, every object has a built-in `__dict__` attribute. This is a dictionary that contains all the attributes defined in the object itself. To see this in action, consider the following example:"
]
},
{
"cell_type": "code",
- "execution_count": 218,
- "id": "magnetic-worker",
+ "execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
@@ -1634,10 +1568,18 @@
" self.color = color"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "my_car = Car('red')"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 219,
- "id": "traditional-travel",
"metadata": {},
"outputs": [
{
@@ -1657,7 +1599,6 @@
},
{
"cell_type": "markdown",
- "id": "dangerous-classic",
"metadata": {},
"source": [
"This code creates a new object and prints the contents of the `__dict__` attribute for both the object and the class. Now, run the script and analyze the output to see the `__dict__` attributes set:\n",
@@ -1670,7 +1611,6 @@
},
{
"cell_type": "markdown",
- "id": "double-knife",
"metadata": {},
"source": [
"The `__dict__` attributes are set as expected. Note that, in Python, **everything is an object**. A class is actually an object as well, so it will also have a `__dict__` attribute that contains all the attributes and methods of the class."
@@ -1679,7 +1619,6 @@
{
"cell_type": "code",
"execution_count": 221,
- "id": "suspected-lincoln",
"metadata": {},
"outputs": [],
"source": [
@@ -1699,7 +1638,6 @@
},
{
"cell_type": "markdown",
- "id": "demonstrated-traveler",
"metadata": {},
"source": [
"In this example, you create an instance of the `Car` class that inherits from the `Vehicle` class. Then, you access some attributes. If you run this example, then you can see that you get all the values you expect."
@@ -1708,7 +1646,6 @@
{
"cell_type": "code",
"execution_count": 223,
- "id": "assigned-introduction",
"metadata": {},
"outputs": [
{
@@ -1729,7 +1666,6 @@
},
{
"cell_type": "markdown",
- "id": "insured-jacob",
"metadata": {},
"source": [
"Here, when you access the attribute `color` of the instance `my_car`, you’re actually accessing a single value of the `__dict__` attribute of the object `my_car`. When you access the attribute `number_of_wheels` of the object `my_car`, you’re really accessing a single value of the `__dict__` attribute of the class `Car`. Finally, when you access the `can_fly` attribute, you’re actually accessing it by using the `__dict__` attribute of the `Vehicle` class."
@@ -1737,7 +1673,6 @@
},
{
"cell_type": "markdown",
- "id": "earlier-blocking",
"metadata": {},
"source": [
"This means that it’s possible to rewrite the above example like this:"
@@ -1746,7 +1681,6 @@
{
"cell_type": "code",
"execution_count": 128,
- "id": "convenient-timer",
"metadata": {},
"outputs": [],
"source": [
@@ -1765,7 +1699,6 @@
{
"cell_type": "code",
"execution_count": 140,
- "id": "indian-administrator",
"metadata": {},
"outputs": [],
"source": [
@@ -1774,7 +1707,6 @@
},
{
"cell_type": "markdown",
- "id": "accepted-beach",
"metadata": {},
"source": [
"When you test this new example, you should get the same result:"
@@ -1783,7 +1715,6 @@
{
"cell_type": "code",
"execution_count": 227,
- "id": "female-alert",
"metadata": {},
"outputs": [
{
@@ -1804,7 +1735,6 @@
},
{
"cell_type": "markdown",
- "id": "guided-anderson",
"metadata": {},
"source": [
"So, what happens when you access the attribute of an object with dot notation? How does the interpreter know what you really need? Well, here’s where a concept called the **lookup chain** comes in:"
@@ -1812,7 +1742,6 @@
},
{
"cell_type": "markdown",
- "id": "material-tower",
"metadata": {},
"source": [
"1. First, you’ll get the result returned from the `__get__` method of the **data descriptor** named after the attribute you’re looking for.\n",
@@ -1832,7 +1761,6 @@
},
{
"cell_type": "markdown",
- "id": "amino-cartridge",
"metadata": {},
"source": [
"Now you can see why it’s important to know if a descriptor is a data descriptor or a non-data descriptor? They’re on different levels of the lookup chain, and you’ll see later on that this difference in behavior can be very convenient."
@@ -1840,15 +1768,14 @@
},
{
"cell_type": "markdown",
- "id": "conventional-festival",
"metadata": {},
"source": [
- "## Why Use Python Descriptors?"
+ "\n",
+ "## Why Use Python Descriptors? [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "lesbian-hundred",
"metadata": {},
"source": [
"Now you know what Python descriptors are and how Python itself uses them to power some of its features, like methods and properties. You’ve also seen how to create a Python descriptor while avoiding some common pitfalls. Everything should be clear now, but you may still wonder why you should use them.\n",
@@ -1858,15 +1785,14 @@
},
{
"cell_type": "markdown",
- "id": "upper-assets",
"metadata": {},
"source": [
- "### Lazy Properties"
+ "\n",
+ "### Lazy Properties [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "brave-workplace",
"metadata": {},
"source": [
"The first and most straightforward example is **lazy properties**. These are properties whose initial values are not loaded until they’re accessed for the first time. Then, they load their initial value and keep that value cached for later reuse."
@@ -1874,7 +1800,6 @@
},
{
"cell_type": "markdown",
- "id": "weighted-pendant",
"metadata": {},
"source": [
"Consider the following example. You have a class `DeepThought` that contains a method `meaning_of_life()` that returns a value after a lot of time spent in heavy concentration:"
@@ -1883,7 +1808,6 @@
{
"cell_type": "code",
"execution_count": 142,
- "id": "grateful-driving",
"metadata": {},
"outputs": [],
"source": [
@@ -1899,7 +1823,6 @@
{
"cell_type": "code",
"execution_count": 143,
- "id": "casual-surprise",
"metadata": {},
"outputs": [],
"source": [
@@ -1909,7 +1832,6 @@
{
"cell_type": "code",
"execution_count": 144,
- "id": "constant-breed",
"metadata": {},
"outputs": [
{
@@ -1927,7 +1849,6 @@
{
"cell_type": "code",
"execution_count": 145,
- "id": "talented-material",
"metadata": {},
"outputs": [
{
@@ -1945,7 +1866,6 @@
{
"cell_type": "code",
"execution_count": 146,
- "id": "removable-receptor",
"metadata": {},
"outputs": [
{
@@ -1962,7 +1882,6 @@
},
{
"cell_type": "markdown",
- "id": "comparable-palestine",
"metadata": {},
"source": [
"If you run this code and try to access the method three times, then you get an answer every three seconds, which is the length of the sleep time inside the method."
@@ -1970,7 +1889,6 @@
},
{
"cell_type": "markdown",
- "id": "understanding-parallel",
"metadata": {},
"source": [
"Now, a lazy property can instead evaluate this method just once when it’s first executed. Then, it will cache the resulting value so that, if you need it again, you can get it in no time. You can achieve this with the use of Python descriptors:"
@@ -1979,7 +1897,6 @@
{
"cell_type": "code",
"execution_count": 159,
- "id": "vulnerable-ranking",
"metadata": {},
"outputs": [],
"source": [
@@ -2005,7 +1922,6 @@
{
"cell_type": "code",
"execution_count": 160,
- "id": "powered-thickness",
"metadata": {},
"outputs": [],
"source": [
@@ -2015,7 +1931,6 @@
{
"cell_type": "code",
"execution_count": 161,
- "id": "interpreted-throat",
"metadata": {},
"outputs": [
{
@@ -2036,7 +1951,6 @@
{
"cell_type": "code",
"execution_count": 162,
- "id": "floppy-bachelor",
"metadata": {},
"outputs": [
{
@@ -2054,7 +1968,6 @@
{
"cell_type": "code",
"execution_count": 163,
- "id": "increased-vatican",
"metadata": {},
"outputs": [
{
@@ -2075,7 +1988,6 @@
{
"cell_type": "code",
"execution_count": 164,
- "id": "committed-injection",
"metadata": {},
"outputs": [
{
@@ -2093,7 +2005,6 @@
{
"cell_type": "code",
"execution_count": 165,
- "id": "cleared-marker",
"metadata": {},
"outputs": [
{
@@ -2110,7 +2021,6 @@
},
{
"cell_type": "markdown",
- "id": "accomplished-origin",
"metadata": {},
"source": [
"Take your time to study this code and understand how it works. Can you see the power of Python descriptors here? In this example, when you use the `@LazyProperty` descriptor, you’re instantiating a descriptor and passing to it `.meaning_of_life()`. This descriptor stores both the method and its name as instance variables."
@@ -2118,7 +2028,6 @@
},
{
"cell_type": "markdown",
- "id": "pressing-table",
"metadata": {},
"source": [
"Since it is a non-data descriptor, when you first access the value of the `meaning_of_life` attribute, `.__get__()` is automatically called and executes `.meaning_of_life()` on the `my_deep_thought_instance` object. The resulting value is stored in the `__dict__` attribute of the object itself. When you access the `meaning_of_life` attribute again, Python will use the **lookup chain** to find a value for that attribute inside the `__dict__` attribute, and that value will be returned immediately."
@@ -2126,7 +2035,6 @@
},
{
"cell_type": "markdown",
- "id": "tough-eclipse",
"metadata": {},
"source": [
"Note that this works because, in this example, you’ve only used one method `.__get__()` of the descriptor protocol. You’ve also implemented a non-data descriptor. If you had implemented a data descriptor, then the trick would not have worked. Following the lookup chain, it would have had precedence over the value stored in `__dict__.` To test this out, run the following code:"
@@ -2135,7 +2043,6 @@
{
"cell_type": "code",
"execution_count": 238,
- "id": "postal-farmer",
"metadata": {},
"outputs": [],
"source": [
@@ -2164,7 +2071,6 @@
{
"cell_type": "code",
"execution_count": 239,
- "id": "civil-links",
"metadata": {},
"outputs": [],
"source": [
@@ -2174,7 +2080,6 @@
{
"cell_type": "code",
"execution_count": 240,
- "id": "congressional-vietnamese",
"metadata": {},
"outputs": [
{
@@ -2192,7 +2097,6 @@
{
"cell_type": "code",
"execution_count": 241,
- "id": "cultural-geometry",
"metadata": {},
"outputs": [
{
@@ -2210,7 +2114,6 @@
{
"cell_type": "code",
"execution_count": 242,
- "id": "quiet-pregnancy",
"metadata": {},
"outputs": [
{
@@ -2227,7 +2130,6 @@
},
{
"cell_type": "markdown",
- "id": "bulgarian-lighter",
"metadata": {},
"source": [
"In this example, you can see that just implementing `.__set__()`, even if it doesn’t do anything at all, creates a data descriptor. Now, the trick of the lazy property stops working."
@@ -2235,15 +2137,14 @@
},
{
"cell_type": "markdown",
- "id": "stuck-island",
"metadata": {},
"source": [
- "### D.R.Y. Code"
+ "\n",
+ "### D.R.Y. Code [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "comic-anniversary",
"metadata": {},
"source": [
"Another typical use case for descriptors is to write reusable code and make your code [D.R.Y.](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) Python descriptors give developers a great tool to write reusable code that can be shared among different properties or even different classes.\n",
@@ -2254,7 +2155,6 @@
{
"cell_type": "code",
"execution_count": 243,
- "id": "undefined-processor",
"metadata": {},
"outputs": [],
"source": [
@@ -2311,7 +2211,6 @@
{
"cell_type": "code",
"execution_count": 245,
- "id": "adequate-bullet",
"metadata": {},
"outputs": [],
"source": [
@@ -2323,7 +2222,6 @@
{
"cell_type": "code",
"execution_count": 246,
- "id": "horizontal-coordinate",
"metadata": {},
"outputs": [
{
@@ -2342,7 +2240,6 @@
},
{
"cell_type": "markdown",
- "id": "intimate-black",
"metadata": {},
"source": [
"As you can see, you have a lot of duplicated code here. It’s possible to use Python descriptors to share behavior among all the properties. You can create an EvenNumber descriptor and use it for all the properties like this:"
@@ -2351,7 +2248,6 @@
{
"cell_type": "code",
"execution_count": 247,
- "id": "different-lebanon",
"metadata": {},
"outputs": [],
"source": [
@@ -2377,7 +2273,6 @@
{
"cell_type": "code",
"execution_count": 248,
- "id": "becoming-insulation",
"metadata": {},
"outputs": [
{
@@ -2399,7 +2294,6 @@
},
{
"cell_type": "markdown",
- "id": "imperial-coordinate",
"metadata": {},
"source": [
"This code looks a lot better now! The duplicates are gone and the logic is now implemented in a single place so that if you need to change it, you can do so easily."
@@ -2407,16 +2301,15 @@
},
{
"cell_type": "markdown",
- "id": "unnecessary-agenda",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
"Now that you know how Python uses descriptors to power some of its great features, you’ll be a more conscious developer who understands why some Python features have been implemented the way they are."
]
},
{
"cell_type": "markdown",
- "id": "defensive-lobby",
"metadata": {},
"source": [
"You’ve learned:\n",
@@ -2428,6 +2321,9 @@
}
],
"metadata": {
+ "interpreter": {
+ "hash": "db458316ab6e40abd61d0cc300c442f9df40dc5a12580a3b20149dedf1c87f1d"
+ },
"kernelspec": {
"display_name": "Python 3",
"language": "python",
@@ -2443,7 +2339,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/04. Advanced/13. `with` Context Manager.ipynb b/Python/04. Advanced/06. `with` Context Manager.ipynb
similarity index 87%
rename from python/04. Advanced/13. `with` Context Manager.ipynb
rename to Python/04. Advanced/06. `with` Context Manager.ipynb
index 15ef762..89a6182 100755
--- a/python/04. Advanced/13. `with` Context Manager.ipynb
+++ b/Python/04. Advanced/06. `with` Context Manager.ipynb
@@ -2,15 +2,36 @@
"cells": [
{
"cell_type": "markdown",
- "id": "careful-warner",
"metadata": {},
"source": [
- "# `with` Context Manager"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
`with` Context Manager \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [Supporting with in Your Own Objects](#supporting_with_in_your_own_objects)\n",
+ "* [Writing Pretty APIs With Context Managers](#writing_pretty_apis_with_context_managers)\n",
+ " * [Solution](#solution)\n",
+ "* [Things to Remember](#things_to_remember)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "american-currency",
"metadata": {},
"source": [
"The `with` statement in Python is regarded as an obscure feature by some. But when you peek behind the scenes of the underlying Context Manager protocol you’ll see there’s little “magic” involved."
@@ -18,7 +39,6 @@
},
{
"cell_type": "markdown",
- "id": "rising-scientist",
"metadata": {},
"source": [
"So what’s the with statement good for? **It helps simplify some common resource management patterns by abstracting their functionality and allowing them to be factored out and reused**."
@@ -26,7 +46,6 @@
},
{
"cell_type": "markdown",
- "id": "peaceful-bachelor",
"metadata": {},
"source": [
"In turn this helps you write more expressive code and makes it easier to avoid resource leaks in your programs."
@@ -34,7 +53,6 @@
},
{
"cell_type": "markdown",
- "id": "dangerous-samba",
"metadata": {},
"source": [
"A good way to see this feature used effectively is by looking at examples in the Python standard library. A well-known example involves the `open()` function:\n",
@@ -47,7 +65,6 @@
},
{
"cell_type": "markdown",
- "id": "rental-frequency",
"metadata": {},
"source": [
"Opening files using the `with` statement is generally recommended because it ensures that open file descriptors are closed automatically after program execution leaves the context of the with statement. Internally, the above code sample translates to something like this:\n",
@@ -63,7 +80,6 @@
},
{
"cell_type": "markdown",
- "id": "remarkable-prototype",
"metadata": {},
"source": [
"You can already tell that this is quite a bit more verbose. Note that the `try...finally` statement is significant. It wouldn’t be enough to just write something like this:\n",
@@ -77,7 +93,6 @@
},
{
"cell_type": "markdown",
- "id": "forced-valentine",
"metadata": {},
"source": [
"This implementation won’t guarantee the file is closed if there’s an exception during the `f.write()` call—and therefore our program might leak a file descriptor. That’s why the with statement is so useful. It makes acquiring and releasing resources properly a breeze."
@@ -85,7 +100,6 @@
},
{
"cell_type": "markdown",
- "id": "breeding-scene",
"metadata": {},
"source": [
"Another good example where the with statement is used effectively in the Python standard library is the `threading.Lock` class (threading and multiprocessing will be covered later):\n",
@@ -108,7 +122,6 @@
},
{
"cell_type": "markdown",
- "id": "subtle-ridge",
"metadata": {},
"source": [
"In both cases using a `with` statement allows you to abstract away most of the resource handling logic. Instead of having to write an explicit `try...finally` statement each time, with takes care of that for us."
@@ -116,7 +129,6 @@
},
{
"cell_type": "markdown",
- "id": "better-queen",
"metadata": {},
"source": [
"The with statement can make code dealing with system resources more readable. It also helps avoid bugs or leaks by making it almost impossible to forget cleaning up or releasing a resource after we’re done with it."
@@ -124,15 +136,14 @@
},
{
"cell_type": "markdown",
- "id": "nervous-factor",
"metadata": {},
"source": [
- "## Supporting with in Your Own Objects"
+ "\n",
+ "## Supporting with in Your Own Objects [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "copyrighted-collectible",
"metadata": {},
"source": [
"Now, there’s nothing special or magical about the `open()` function and the fact that they can be used with a with statement. You can provide the same functionality in your own classes and functions by implementing so-called context managers."
@@ -140,7 +151,6 @@
},
{
"cell_type": "markdown",
- "id": "meaningful-calculator",
"metadata": {},
"source": [
"What’s a context manager? It’s a simple “protocol” (or interface) that your object needs to follow so it can be used with the with statement. Basically all you need to do is add `__enter__` and `__exit__` methods to an object if you want it to function as a context manager. Python will call these two methods at the appropriate times in the resource management cycle."
@@ -148,7 +158,6 @@
},
{
"cell_type": "markdown",
- "id": "possible-holmes",
"metadata": {},
"source": [
"Let’s take a look at what this would look like in practical terms. Here’s how a simple implementation of the open() context manager might look like:"
@@ -157,7 +166,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "romantic-minimum",
"metadata": {},
"outputs": [],
"source": [
@@ -178,7 +186,6 @@
},
{
"cell_type": "markdown",
- "id": "positive-mapping",
"metadata": {},
"source": [
"Our `ManagedFile` class follows the context manager protocol and now supports the with statement, just like the original `open()` example did:"
@@ -187,7 +194,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "mechanical-safety",
"metadata": {},
"outputs": [
{
@@ -207,7 +213,6 @@
},
{
"cell_type": "markdown",
- "id": "curious-reset",
"metadata": {},
"source": [
"Python calls `__enter__` when execution enters the context of the with statement and it’s time to acquire the resource. When execution leaves the context again, Python calls `__exit__` to free up the resource."
@@ -215,7 +220,6 @@
},
{
"cell_type": "markdown",
- "id": "outstanding-sigma",
"metadata": {},
"source": [
"Writing a class-based context manager isn’t the only way to support the `with` statement in Python. The `contextlib` utility module in the standard library provides a few more abstractions built on top of the basic context manager protocol. This can make your life a little easier if your use cases matches what’s offered by contextlib."
@@ -223,7 +227,6 @@
},
{
"cell_type": "markdown",
- "id": "intellectual-discretion",
"metadata": {},
"source": [
"For example, you can use the `contextlib.contextmanager` decorator to define a generator-based factory function for a resource that will then automatically support the with statement. Here’s what rewriting our `ManagedFile` context manager with this technique looks like:"
@@ -232,7 +235,6 @@
{
"cell_type": "code",
"execution_count": 32,
- "id": "useful-shooting",
"metadata": {},
"outputs": [],
"source": [
@@ -250,7 +252,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "signal-slide",
"metadata": {},
"outputs": [],
"source": [
@@ -261,7 +262,6 @@
},
{
"cell_type": "markdown",
- "id": "aboriginal-income",
"metadata": {},
"source": [
"In this case, `managed_file()` is a generator that first acquires the resource. Then it temporarily suspends its own executing and yields the resource so it can be used by the caller. When the caller leaves the with context, the generator continues to execute so that any remaining clean up steps can happen and the resource gets released back to the system."
@@ -269,7 +269,6 @@
},
{
"cell_type": "markdown",
- "id": "speaking-guest",
"metadata": {},
"source": [
"Both the class-based implementations and the generator-based are practically equivalent. Depending on which one you find more readable you might prefer one over the other."
@@ -277,7 +276,6 @@
},
{
"cell_type": "markdown",
- "id": "single-union",
"metadata": {},
"source": [
"A downside of the `@contextmanager`-based implementation might be that it requires understanding of advanced Python concepts, like decorators and generators."
@@ -285,7 +283,6 @@
},
{
"cell_type": "markdown",
- "id": "informed-driving",
"metadata": {},
"source": [
"Once again, making the right choice here comes down to what you and your team are comfortable using and find the most readable."
@@ -293,15 +290,14 @@
},
{
"cell_type": "markdown",
- "id": "comfortable-recipe",
"metadata": {},
"source": [
- "## Writing Pretty APIs With Context Managers"
+ "\n",
+ "## Writing Pretty APIs With Context Managers [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "designed-terror",
"metadata": {},
"source": [
"Context managers are quite flexible and if you use the with statement creatively you can define convenient APIs for your modules and classes."
@@ -309,7 +305,6 @@
},
{
"cell_type": "markdown",
- "id": "material-durham",
"metadata": {},
"source": [
"For example, what if the “resource” we wanted to manage was text indentation levels in some kind of report generator program? What if we could write code like this to do it:\n",
@@ -325,36 +320,8 @@
"```"
]
},
- {
- "cell_type": "code",
- "execution_count": 45,
- "id": "given-bahamas",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " hi!\n",
- " hello\n",
- " bonjour\n",
- " hey\n"
- ]
- }
- ],
- "source": [
- "with Indenter() as indent:\n",
- " indent.print('hi!')\n",
- " with indent:\n",
- " indent.print('hello')\n",
- " with indent:\n",
- " indent.print('bonjour')\n",
- " indent.print('hey')"
- ]
- },
{
"cell_type": "markdown",
- "id": "oriental-hebrew",
"metadata": {},
"source": [
"This almost reads like a domain-specific language (DSL) for indenting text. Also, notice how this code enters and leaves the same context manager multiple times to change indentation levels. Running this code snippet should lead to the following output and print neatly formatted text:\n",
@@ -369,7 +336,6 @@
},
{
"cell_type": "markdown",
- "id": "structured-sussex",
"metadata": {},
"source": [
"How would you implement a context manager to support this functionality?"
@@ -377,7 +343,6 @@
},
{
"cell_type": "markdown",
- "id": "velvet-terrace",
"metadata": {},
"source": [
"By the way, this could be a great exercise to wrap your head around how context managers work. So before you check out my implementation below you might take some time and try to implement this yourself as a learning exercise."
@@ -385,15 +350,14 @@
},
{
"cell_type": "markdown",
- "id": "coated-planner",
"metadata": {},
"source": [
- "### Solution"
+ "\n",
+ "### Solution [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "hidden-binding",
"metadata": {},
"source": [
"Ready? Here’s how we might implement this functionality using a class-based context manager:"
@@ -402,7 +366,6 @@
{
"cell_type": "code",
"execution_count": 44,
- "id": "composite-trout",
"metadata": {},
"outputs": [],
"source": [
@@ -424,7 +387,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "impressive-radio",
"metadata": {},
"outputs": [
{
@@ -450,7 +412,6 @@
},
{
"cell_type": "markdown",
- "id": "recovered-watch",
"metadata": {},
"source": [
"Another good exercise would be trying to refactor this code to be generator-based."
@@ -458,10 +419,10 @@
},
{
"cell_type": "markdown",
- "id": "green-sampling",
"metadata": {},
"source": [
- "## Things to Remember\n",
+ "\n",
+ "## Things to Remember [
](#table_of_contents)\n",
"- The `with` statement simplifies exception handling by encapsulating standard uses of `try/finally` statements in so-called Context Managers.\n",
"- Most commonly it is used to manage the safe acquisition and release of system resources. Resources are acquired by the `with` statement and released automatically when execution leaves the `with` context.\n",
"- Using `with` effectively can help you avoid resource leaks and make your code easier to read."
@@ -484,7 +445,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/Python/04. Advanced/07.1 Speed Up Your Python Program With Concurrency.ipynb b/Python/04. Advanced/07.1 Speed Up Your Python Program With Concurrency.ipynb
new file mode 100644
index 0000000..7193e17
--- /dev/null
+++ b/Python/04. Advanced/07.1 Speed Up Your Python Program With Concurrency.ipynb
@@ -0,0 +1,508 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Speed Up Your Python Program With Concurrency \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "##
Table of Contents \n",
+ "* [What Is Concurrency?](#what_is_concurrency?)\n",
+ "* [What Is Parallelism?](#what_is_parallelism?)\n",
+ "* [When Is Concurrency Useful?](#when_is_concurrency_useful?)\n",
+ "* [How to Speed Up an I/O-Bound Program](#how_to_speed_up_an_i/o-bound_program)\n",
+ " * [Synchronous Version](#synchronous_version)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you’ve heard lots of talk about `asyncio` [being added to Python](https://realpython.com/python37-new-features/) but are curious how it compares to other concurrency methods or are wondering what concurrency is and how it might speed up your program, you’ve come to the right place.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**In this article, you’ll learn the following:**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- What **concurrency** is\n",
+ "- What **parallelism** is\n",
+ "- How some of **Python’s concurrency methods** compare, including `threading`, `asyncio`, and `multiprocessing`\n",
+ "- **When to use concurrency** in your program and which module to use\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This article assumes that you have a basic understanding of Python and that you’re using at least version 3.6 to run the examples. You can download the examples from the [*Real Python* GitHub repo](https://github.com/realpython/materials/tree/master/concurrency-overview).\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## What Is Concurrency? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The dictionary definition of concurrency is simultaneous occurrence. In Python, the things that are occurring simultaneously are called by different names (thread, task, process) but at a high level, they all refer to a sequence of instructions that run in order.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "I like to think of them as different trains of thought. Each one can be stopped at certain points, and the CPU or brain that is processing them can switch to a different one. The state of each one is saved so it can be restarted right where it was interrupted.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You might wonder why Python uses different words for the same concept. It turns out that threads, tasks, and processes are only the same if you view them from a high level. Once you start digging into the details, they all represent slightly different things. You’ll see more of how they are different as you progress through the examples.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now let’s talk about the simultaneous part of that definition. You have to be a little careful because, when you get down to the details, only `multiprocessing` actually runs these trains of thought at literally the same time. [`Threading`](https://realpython.com/intro-to-python-threading/) and `asyncio` both run on a single processor and therefore only run one at a time. They just cleverly find ways to take turns to speed up the overall process. Even though they don’t run different trains of thought simultaneously, we still call this concurrency.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The way the threads or tasks take turns is the big difference between `threading` and `asyncio`. In `threading`, the operating system actually knows about each thread and can interrupt it at any time to start running a different thread. This is called [pre-emptive multitasking](https://en.wikipedia.org/wiki/Preemption\\\\_%28computing%29#Preemptive\\\\_multitasking) since the operating system can pre-empt your thread to make the switch.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Pre-emptive multitasking is handy in that the code in the thread doesn’t need to do anything to make the switch. It can also be difficult because of that “at any time” phrase. This switch can happen in the middle of a single Python statement, even a trivial one like `x = x + 1`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`Asyncio`, on the other hand, uses [cooperative multitasking](https://en.wikipedia.org/wiki/Cooperative\\\\_multitasking). The tasks must cooperate by announcing when they are ready to be switched out. That means that the code in the task has to change slightly to make this happen.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The benefit of doing this extra work up front is that you always know where your task will be swapped out. It will not be swapped out in the middle of a Python statement unless that statement is marked. You’ll see later how this can simplify parts of your design.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## What Is Parallelism? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So far, you’ve looked at concurrency that happens on a single processor. What about all of those CPU cores your cool, new laptop has? How can you make use of them? `multiprocessing` is the answer.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "With `multiprocessing`, Python creates new processes. A process here can be thought of as almost a completely different program, though technically they’re usually defined as a collection of resources where the resources include memory, file handles and things like that. One way to think about it is that each process runs in its own Python interpreter.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Because they are different processes, each of your trains of thought in a multiprocessing program can run on a different core. Running on a different core means that they actually can run at the same time, which is fabulous. There are some complications that arise from doing this, but Python does a pretty good job of smoothing them over most of the time.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that you have an idea of what concurrency and parallelism are, let’s review their differences, and then we can look at why they can be useful:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "|Concurrency Type|Switching Decision|Number of Processors|\n",
+ "|:--|:--|:--|\n",
+ "|Pre-emptive multitasking (`threading`)|The operating system decides when to switch tasks external to Python.|1|\n",
+ "|Cooperative multitasking (`asyncio`)|The tasks decide when to give up control.|1|\n",
+ "|Multiprocessing (`multiprocessing`)|The processes all run at the same time on different processors.|Many|\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Each of these types of concurrency can be useful. Let’s take a look at what types of programs they can help you speed up.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## When Is Concurrency Useful? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Concurrency can make a big difference for two types of problems. These are generally called CPU-bound and I/O-bound.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "I/O-bound problems cause your program to slow down because it frequently must wait for [input/output](https://realpython.com/python-input-output/) (I/O) from some external resource. They arise frequently when your program is working with things that are much slower than your CPU.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Examples of things that are slower than your CPU are legion, but your program thankfully does not interact with most of them. The slow things your program will interact with most frequently are the file system and network connections.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s see what that looks like:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the diagram above, the blue boxes show time when your program is doing work, and the red boxes are time spent waiting for an I/O operation to complete. This diagram is not to scale because requests on the internet can take several orders of magnitude longer than CPU instructions, so your program can end up spending most of its time waiting. This is what your browser is doing most of the time.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "On the flip side, there are classes of programs that do significant computation without talking to the network or accessing a file. These are the CPU-bound programs, because the resource limiting the speed of your program is the CPU, not the network or the file system.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s a corresponding diagram for a CPU-bound program:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you work through the examples in the following section, you’ll see that different forms of concurrency work better or worse with CPU-bound and I/O-bound programs. Adding concurrency to your program adds extra code and complications, so you’ll need to decide if the potential speed up is worth the extra effort. By the end of this article, you should have enough info to start making that decision.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s a quick summary to clarify this concept:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "|I/O-Bound Process|CPU-Bound Process|\n",
+ "|:--|:--|\n",
+ "|Your program spends most of its time talking to a slow device, like a network connection, a hard drive, or a printer.|You program spends most of its time doing CPU operations.|\n",
+ "|Speeding it up involves overlapping the times spent waiting for these devices.|Speeding it up involves finding ways to do more computations in the same amount of time.|\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You’ll look at I/O-bound programs first. Then, you’ll get to see some code dealing with CPU-bound programs.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## How to Speed Up an I/O-Bound Program [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s start by focusing on I/O-bound programs and a common problem: downloading content over the network. For our example, you will be downloading web pages from a few sites, but it really could be any network traffic. It’s just easier to visualize and set up with web pages.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "### Synchronous Version [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We’ll start with a non-concurrent version of this task. Note that this program requires the [`requests`](http://docs.python-requests.org/en/master/) module. You should run `pip install requests` before running it, probably using a [virtualenv](https://realpython.com/python-virtual-environments-a-primer/). This version does not use concurrency at all:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import requests\n",
+ "import time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def download_site(url, session):\n",
+ " with session.get(url) as response:\n",
+ " print(f\"Read {len(response.content)} from {url}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def download_all_sites(sites):\n",
+ " with requests.Session() as session:\n",
+ " for url in sites:\n",
+ " download_site(url, session)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sites = [\n",
+ " \"https://www.jython.org\",\n",
+ " \"http://olympus.realpython.org/dice\",\n",
+ "] * 80\n",
+ "start_time = time.time()\n",
+ "download_all_sites(sites)\n",
+ "duration = time.time() - start_time\n",
+ "print(f\"Downloaded {len(sites)} in {duration} seconds\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you can see, this is a fairly short program. `download_site()` just downloads the contents from a URL and prints the size. One small thing to point out is that we’re using a [`Session`](https://2.python-requests.org/en/master/user/advanced/#id1) object from `requests`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It is possible to simply use `get()` from `requests` directly, but creating a `Session` object allows `requests` to do some fancy networking tricks and really speed things up.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`download_all_sites()` creates the `Session` and then walks through the [list](https://realpython.com/python-lists-tuples/) of sites, downloading each one in turn. Finally, it prints out how long this process took so you can have the satisfaction of seeing how much concurrency has helped us in the following examples.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The processing diagram for this program will look much like the I/O-bound diagram in the last section.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Why the Synchronous Version Rocks**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The great thing about this version of code is that, well, it’s easy. It was comparatively easy to write and debug. It’s also more straight-forward to think about. There’s only one train of thought running through it, so you can predict what the next step is and how it will behave.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**The Problems With the Synchronous Version**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The big problem here is that it’s relatively slow compared to the other solutions we’ll provide. Here’s an example of what the final output gave on my machine:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./io_non_concurrent.py\n",
+ " [most output skipped]\n",
+ "Downloaded 160 in 14.289619207382202 seconds\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Being slower isn’t always a big issue, however. If the program you’re running takes only 2 seconds with a synchronous version and is only run rarely, it’s probably not worth adding concurrency. You can stop here.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What if your program is run frequently? What if it takes hours to run? Let’s move on to concurrency in the next sections by rewriting this program using `threading`.\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.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/Python/04. Advanced/07.2 An Intro to Threading in Python.ipynb b/Python/04. Advanced/07.2 An Intro to Threading in Python.ipynb
new file mode 100644
index 0000000..623f3a9
--- /dev/null
+++ b/Python/04. Advanced/07.2 An Intro to Threading in Python.ipynb
@@ -0,0 +1,2162 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
An Intro to Threading in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "##
Table of Contents \n",
+ "* [Download Sites: Synchronous Version](#download_sites:_synchronous_version)\n",
+ "* [What Is a Thread?](#what_is_a_thread?)\n",
+ "* [Starting a Thread](#starting_a_thread)\n",
+ " * [Daemon Threads](#daemon_threads)\n",
+ " * [`join()` a Thread](#`join()`_a_thread)\n",
+ "* [Working With Many Threads](#working_with_many_threads)\n",
+ "* [Using a ThreadPoolExecutor](#using_a_threadpoolexecutor)\n",
+ "* [Race Conditions](#race_conditions)\n",
+ " * [One Thread](#one_thread)\n",
+ " * [Two Threads](#two_threads)\n",
+ " * [Why This Isn’t a Silly Example](#why_this_isn’t_a_silly_example)\n",
+ " * [How Does This Really Work](#how_does_this_really_work)\n",
+ "* [Basic Synchronization Using Lock](#basic_synchronization_using_lock)\n",
+ "* [Deadlock](#deadlock)\n",
+ "* [Threading Objects](#threading_objects)\n",
+ " * [Semaphore](#semaphore)\n",
+ " * [Timer](#timer)\n",
+ " * [Barrier](#barrier)\n",
+ "* [Download Sites: Threading Version](#download_sites:_threading_version)\n",
+ "* [Conclusion: Threading in Python](#conclusion:_threading_in_python)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s start by focusing on I/O-bound programs and a common problem: downloading content over the network. For our example, you will be downloading web pages from a few sites, but it really could be any network traffic. It’s just easier to visualize and set up with web pages.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## Download Sites: Synchronous Version [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We’ll start with a non-concurrent version of this task. Note that this program requires the [`requests`](http://docs.python-requests.org/en/master/) module. You should run `pip install requests` before running it, probably using a [virtualenv](https://realpython.com/python-virtual-environments-a-primer/). This version does not use concurrency at all:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import requests\n",
+ "import time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def download_site(url, session):\n",
+ " with session.get(url) as response:\n",
+ " print(f\"Read {len(response.content)} from {url}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def download_all_sites(sites):\n",
+ " with requests.Session() as session:\n",
+ " for url in sites:\n",
+ " download_site(url, session)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sites = [\n",
+ " \"https://www.jython.org\",\n",
+ " \"http://olympus.realpython.org/dice\",\n",
+ "] * 80\n",
+ "start_time = time.time()\n",
+ "download_all_sites(sites)\n",
+ "duration = time.time() - start_time\n",
+ "print(f\"Downloaded {len(sites)} in {duration} seconds\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you can see, this is a fairly short program. `download_site()` just downloads the contents from a URL and prints the size. One small thing to point out is that we’re using a [`Session`](https://2.python-requests.org/en/master/user/advanced/#id1) object from `requests`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It is possible to simply use `get()` from `requests` directly, but creating a `Session` object allows `requests` to do some fancy networking tricks and really speed things up.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`download_all_sites()` creates the `Session` and then walks through the [list](https://realpython.com/python-lists-tuples/) of sites, downloading each one in turn. Finally, it prints out how long this process took so you can have the satisfaction of seeing how much concurrency has helped us in the following examples.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The processing diagram for this program will look much like the I/O-bound diagram in the last section.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Why the Synchronous Version Rocks**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The great thing about this version of code is that, well, it’s easy. It was comparatively easy to write and debug. It’s also more straight-forward to think about. There’s only one train of thought running through it, so you can predict what the next step is and how it will behave.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**The Problems With the Synchronous Version**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The big problem here is that it’s relatively slow compared to the other solutions we’ll provide. Here’s an example of what the final output gave on my machine:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./io_non_concurrent.py\n",
+ " [most output skipped]\n",
+ "Downloaded 160 in 14.289619207382202 seconds\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Being slower isn’t always a big issue, however. If the program you’re running takes only 2 seconds with a synchronous version and is only run rarely, it’s probably not worth adding concurrency. You can stop here.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "What if your program is run frequently? What if it takes hours to run? Let’s move on to concurrency by rewriting this program using `threading`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python threading allows you to have different parts of your program run concurrently and can simplify your design. If you’ve got some experience in Python and want to speed up your program using threads, then this tutorial is for you!\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**In this article, you’ll learn:**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- What threads are\n",
+ "- How to create threads and wait for them to finish\n",
+ "- How to use a `ThreadPoolExecutor`\n",
+ "- How to avoid race conditions\n",
+ "- How to use the common tools that Python `threading` provides\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## What Is a Thread? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A thread is a separate flow of execution. This means that your program will have two things happening at once. But for most Python 3 implementations the different threads do not actually execute at the same time: they merely appear to.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It’s tempting to think of threading as having two (or more) different processors running on your program, each one doing an independent task at the same time. That’s almost right. The threads may be running on different processors, but they will only be running one at a time. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Getting multiple tasks running simultaneously requires a non-standard implementation of Python, writing some of your code in a different language, or using `multiprocessing` which comes with some extra overhead.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Because of the way CPython implementation of Python works, threading may not speed up all tasks. This is due to interactions with the GIL that essentially limit one Python thread to run at a time.\n",
+ "\n",
+ "\n",
+ "The Python Global Interpreter Lock or [GIL](https://wiki.python.org/moin/GlobalInterpreterLock), in simple words, is a mutex (or a lock) that allows only one thread to hold the control of the Python interpreter. You will read more about GIL and how it works in the upcoming sections. This means that only one thread can be in a state of execution at any point in time. The impact of the GIL isn’t visible to developers who execute single-threaded programs, but it can be a performance bottleneck in CPU-bound and multi-threaded code. GIL will be covered in details in the upcoming sections.\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Tasks that spend much of their time waiting for external events are generally good candidates for threading. Problems that require heavy CPU computation and spend little time waiting for external events might not run faster at all. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is true for code written in Python and running on the standard CPython implementation. If your threads are written in C they have the ability to release the GIL and run concurrently. If you are running on a different Python implementation, check with the documentation too see how it handles threads. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you are running a standard Python implementation, writing in only Python, and have a CPU-bound problem, you should check out the `multiprocessing` module instead.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Architecting your program to use threading can also provide gains in design clarity. Most of the examples you’ll learn about in this tutorial are not necessarily going to run faster because they use threads. Using threading in them helps to make the design cleaner and easier to reason about.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So, let’s stop talking about threading and start using it!\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Starting a Thread [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that you’ve got an idea of what a thread is, let’s learn how to make one. The Python standard library provides [`threading`](https://docs.python.org/3/library/threading.html), which contains most of the primitives you’ll see in this article. `Thread`, in this module, nicely encapsulates threads, providing a clean interface to work with them.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To start a separate thread, you create a `Thread` instance and then tell it to `.start()`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import logging\n",
+ "import threading\n",
+ "import time\n",
+ "\n",
+ "def thread_function(name):\n",
+ " logging.info(f\"Thread {name}: starting\")\n",
+ " time.sleep(2)\n",
+ " logging.info(f\"Thread {name}: finishing\")\n",
+ "\n",
+ "if __name__ == \"__main__\":\n",
+ " format = \"%(asctime)s: %(message)s\"\n",
+ " logging.basicConfig(format=format, level=logging.INFO, datefmt=\"%H:%M:%S\")\n",
+ "\n",
+ " logging.info(\"Main : before creating thread\")\n",
+ " x = threading.Thread(target=thread_function, args=(1,))\n",
+ " logging.info(\"Main : before running thread\")\n",
+ " x.start()\n",
+ " logging.info(\"Main : wait for the thread to finish\")\n",
+ " # x.join()\n",
+ " logging.info(\"Main : all done\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you look around the [logging](https://realpython.com/python-logging/) statements, you can see that the `main` section is creating and starting the thread:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = threading.Thread(target=thread_function, args=(1,))\n",
+ "x.start()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you create a `Thread`, you pass it a function and a list containing the arguments to that function. In this case, you’re telling the `Thread` to run `thread_function()` and to pass it `1` as an argument.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For this article, you’ll use sequential integers as names for your threads. There is `threading.get_ident()`, which returns a unique name for each thread, but these are usually neither short nor easily readable.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`thread_function()` itself doesn’t do much. It simply logs some messages with a [`time.sleep()`](https://realpython.com/python-sleep/) in between them.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you run this program as it is (with line twenty commented out), the output will look like this:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./single_thread.py\n",
+ "Main : before creating thread\n",
+ "Main : before running thread\n",
+ "Thread 1: starting\n",
+ "Main : wait for the thread to finish\n",
+ "Main : all done\n",
+ "Thread 1: finishing\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You’ll notice that the `Thread` finished after the `Main` section of your code did. You’ll come back to why that is and talk about the mysterious line twenty (`x.join()`) in the next section.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Daemon Threads [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In computer science, a [`daemon`](https://en.wikipedia.org/wiki/Daemon\\\\_(computing)) is a process that runs in the background.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python `threading` has a more specific meaning for `daemon`. A `daemon` thread will shut down immediately when the program exits. One way to think about these definitions is to consider the `daemon` thread a thread that runs in the background without worrying about shutting it down.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If a program is running `Threads` that are not `daemons`, then the program will wait for those threads to complete before it terminates. `Threads` that *are* daemons, however, are just killed wherever they are when the program is exiting.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s look a little more closely at the output of your program above. The last two lines are the interesting bit. When you run the program, you’ll notice that there is a pause (of about 2 seconds) after [`__main__`](https://realpython.com/python-main-function/) has printed its `all done` message and before the thread is finished.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This pause is Python waiting for the non-daemonic thread to complete. When your Python program ends, part of the shutdown process is to clean up the threading routine.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you look at the [source for Python `threading`](https://github.com/python/cpython/blob/df5cdc11123a35065bbf1636251447d0bfe789a5/Lib/threading.py#L1263), you’ll see that `threading._shutdown()` walks through all of the running threads and calls `.join()` on every one that does not have the `daemon` flag set.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So your program waits to exit because the thread itself is waiting in a sleep. As soon as it has completed and printed the message, `.join()` will return and the program can exit.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Frequently, this behavior is what you want, but there are other options available to us. Let’s first repeat the program with a `daemon` thread. You do that by changing how you construct the `Thread`, adding the `daemon=True` flag:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = threading.Thread(target=thread_function, args=(1,), daemon=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you run the program now, you should see this output:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./daemon_thread.py\n",
+ "Main : before creating thread\n",
+ "Main : before running thread\n",
+ "Thread 1: starting\n",
+ "Main : wait for the thread to finish\n",
+ "Main : all done\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The difference here is that the final line of the output is missing. `thread_function()` did not get a chance to complete. It was a `daemon` thread, so when `__main__` reached the end of its code and the program wanted to finish, the daemon was killed.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### `join()` a Thread [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Daemon threads are handy, but what about when you want to wait for a thread to stop? What about when you want to do that and not exit your program? Now let’s go back to your original program and look at that commented out line twenty:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# x.join()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To tell one thread to wait for another thread to finish, you call `.join()`. If you uncomment that line, the main thread will pause and wait for the thread `x` to complete running.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Did you test this on the code with the daemon thread or the regular thread? It turns out that it doesn’t matter. If you `.join()` a thread, that statement will wait until either kind of thread is finished.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Working With Many Threads [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The example code so far has only been working with two threads: the main thread and one you started with the `threading.Thread` object. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Frequently, you’ll want to start a number of threads and have them do interesting work. Let’s start by looking at the harder way of doing that, and then you’ll move on to an easier method.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The harder way of starting multiple threads is the one you already know:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import logging\n",
+ "import threading\n",
+ "import time\n",
+ "\n",
+ "def thread_function(name):\n",
+ " logging.info(\"Thread %s: starting\", name)\n",
+ " time.sleep(2)\n",
+ " logging.info(\"Thread %s: finishing\", name)\n",
+ "\n",
+ "if __name__ == \"__main__\":\n",
+ " format = \"%(asctime)s: %(message)s\"\n",
+ " logging.basicConfig(format=format, level=logging.INFO,\n",
+ " datefmt=\"%H:%M:%S\")\n",
+ "\n",
+ " threads = list()\n",
+ " for index in range(3):\n",
+ " logging.info(\"Main : create and start thread %d.\", index)\n",
+ " x = threading.Thread(target=thread_function, args=(index,))\n",
+ " threads.append(x)\n",
+ " x.start()\n",
+ "\n",
+ " for index, thread in enumerate(threads):\n",
+ " logging.info(\"Main : before joining thread %d.\", index)\n",
+ " thread.join()\n",
+ " logging.info(\"Main : thread %d done\", index)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This code uses the same mechanism you saw above to start a thread, create a `Thread` object, and then call `.start()`. The program keeps a list of `Thread` objects so that it can then wait for them later using `.join()`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running this code multiple times will likely produce some interesting results. Here’s an example output from my machine:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./multiple_threads.py\n",
+ "Main : create and start thread 0.\n",
+ "Thread 0: starting\n",
+ "Main : create and start thread 1.\n",
+ "Thread 1: starting\n",
+ "Main : create and start thread 2.\n",
+ "Thread 2: starting\n",
+ "Main : before joining thread 0.\n",
+ "Thread 2: finishing\n",
+ "Thread 1: finishing\n",
+ "Thread 0: finishing\n",
+ "Main : thread 0 done\n",
+ "Main : before joining thread 1.\n",
+ "Main : thread 1 done\n",
+ "Main : before joining thread 2.\n",
+ "Main : thread 2 done\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you walk through the output carefully, you’ll see all three threads getting started in the order you might expect, but in this case they finish in the opposite order! Multiple runs will produce different orderings. Look for the `Thread x: finishing` message to tell you when each thread is done.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The order in which threads are run is determined by the operating system and can be quite hard to predict. It may (and likely will) vary from run to run, so you need to be aware of that when you design algorithms that use threading.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Fortunately, Python gives you several primitives that you’ll look at later to help coordinate threads and get them running together. Before that, let’s look at how to make managing a group of threads a bit easier.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Using a ThreadPoolExecutor [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There’s an easier way to start up a group of threads than the one you saw above. It’s called a `ThreadPoolExecutor`, and it’s part of the standard library in [`concurrent.futures`](https://docs.python.org/3/library/concurrent.futures.html) (as of Python 3.2).\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The easiest way to create it is as a context manager, using the [`with` statement](https://realpython.com/python-with-statement/) to manage the creation and destruction of the pool.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s the `__main__` from the last example rewritten to use a `ThreadPoolExecutor`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import concurrent.futures\n",
+ "\n",
+ "# [rest of code]\n",
+ "\n",
+ "if __name__ == \"__main__\":\n",
+ " format = \"%(asctime)s: %(message)s\"\n",
+ " logging.basicConfig(\n",
+ " format=format, level=logging.INFO, datefmt=\"%H:%M:%S\"\n",
+ " )\n",
+ "\n",
+ " with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:\n",
+ " executor.map(thread_function, range(3))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The code creates a `ThreadPoolExecutor` as a context manager, telling it how many worker threads it wants in the pool. It then uses `.map()` to step through an iterable of things, in your case `range(3)`, passing each one to a thread in the pool. You can think of `concurrent.map` as just a parallel version of the builtin `map` function.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The end of the `with` block causes the `ThreadPoolExecutor` to do a `.join()` on each of the threads in the pool. It is *strongly* recommended that you use `ThreadPoolExecutor` as a context manager when you can so that you never forget to `.join()` the threads.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running your corrected example code will produce output that looks like this:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./executor.py\n",
+ "Thread 0: starting\n",
+ "Thread 1: starting\n",
+ "Thread 2: starting\n",
+ "Thread 1: finishing\n",
+ "Thread 0: finishing\n",
+ "Thread 2: finishing\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Again, notice how `Thread 1` finished before `Thread 0`. The scheduling of threads is done by the operating system and does not follow a plan that’s easy to figure out.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Race Conditions [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before you move on to some of the other features tucked away in Python `threading`, let’s talk a bit about one of the more difficult issues you’ll run into when writing threaded programs: [race conditions](https://en.wikipedia.org/wiki/Race\\\\_condition).\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Once you’ve seen what a race condition is and looked at one happening, you’ll move on to some of the primitives provided by the standard library to prevent race conditions from happening.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Race conditions can occur when two or more threads access a shared piece of data or resource. In this example, you’re going to create a large race condition that happens every time, but be aware that most race conditions are not this obvious. Frequently, they only occur rarely, and they can produce confusing results. As you can imagine, **this makes them quite difficult to debug**.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Fortunately, this race condition will happen every time, and you’ll walk through it in detail to explain what is happening.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For this example, you’re going to write a class that updates a database. Okay, you’re not really going to have a database: you’re just going to fake it, because that’s not the point of this article.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Your `FakeDatabase` will have `.__init__()` and `.update()` methods:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class FakeDatabase:\n",
+ " def __init__(self):\n",
+ " self.value = 0\n",
+ "\n",
+ " def update(self, name):\n",
+ " logging.info(f\"Thread {name}: starting update\")\n",
+ " local_copy = self.value\n",
+ " local_copy += 1\n",
+ " time.sleep(0.1)\n",
+ " self.value = local_copy\n",
+ " logging.info(f\"Thread {name}: finishing update\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`FakeDatabase` is keeping track of a single number: `.value`. This is going to be the shared data on which you’ll see the race condition.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`.__init__()` simply initializes `.value` to zero. So far, so good.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`.update()` looks a little strange. It’s simulating reading a value from a database, doing some computation on it, and then writing a new value back to the database.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this case, reading from the database just means copying `.value` to a local variable. The computation is just to add one to the value and then `.sleep()` for a little bit. Finally, it writes the value back by copying the local value back to `.value`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s how you’ll use this `FakeDatabase`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "format = \"%(asctime)s: %(message)s\"\n",
+ "logging.basicConfig(format=format, level=logging.INFO, datefmt=\"%H:%M:%S\")\n",
+ "\n",
+ "database = FakeDatabase()\n",
+ "logging.info(f\"Testing update. Starting value is {database.value}.\")\n",
+ "with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:\n",
+ " for ® in range(2):\n",
+ " executor.submit(database.update, index)\n",
+ "logging.info(f\"Testing update. Ending value is {database.value}.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The program creates a `ThreadPoolExecutor` with two threads and then calls `.submit()` on each of them, telling them to run `database.update()`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`.submit()` has a signature that allows both positional and named arguments to be passed to the function running in the thread:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ ".submit(function, *args, **kwargs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the usage above, `index` is passed as the first and only positional argument to `database.update()`. You’ll see later in this article where you can pass multiple arguments in a similar manner.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Since each thread runs `.update()`, and `.update()` adds one to `.value`, you might expect `database.value` to be `2` when it’s printed out at the end. But you wouldn’t be looking at this example if that was the case. If you run the above code, the output looks like this:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./racecond.py\n",
+ "Testing unlocked update. Starting value is 0.\n",
+ "Thread 0: starting update\n",
+ "Thread 1: starting update\n",
+ "Thread 0: finishing update\n",
+ "Thread 1: finishing update\n",
+ "Testing unlocked update. Ending value is 1.\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You might have expected that to happen, but let’s look at the details of what’s really going on here, as that will make the solution to this problem easier to understand.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### One Thread [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before you dive into this issue with two threads, let’s step back and talk a bit about some details of how threads work.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You won’t be diving into all of the details here, as that’s not important at this level. We’ll also be simplifying a few things in a way that won’t be technically accurate but will give you the right idea of what is happening.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you tell your `ThreadPoolExecutor` to run each thread, you tell it which function to run and what parameters to pass to it: `executor.submit(database.update, index)`. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The result of this is that each of the threads in the pool will call `database.update(index)`. Note that `database` is a reference to the one `FakeDatabase` object created in `__main__`. Calling `.update()` on that object calls an [instance method](https://realpython.com/instance-class-and-static-methods-demystified/) on that object.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Each thread is going to have a reference to the same `FakeDatabase` object, `database`. Each thread will also have a unique value, `index`, to make the logging statements a bit easier to read:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When the thread starts running `.update()`, it has its own version of all of the data **local** to the function. In the case of `.update()`, this is `local_copy`. This is definitely a good thing. Otherwise, two threads running the same function would always confuse each other. It means that all variables that are scoped (or local) to a function are **thread-safe**.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now you can start walking through what happens if you run the program above with a single thread and a single call to `.update()`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The image below steps through the execution of `.update()` if only a single thread is run. The statement is shown on the left followed by a diagram showing the values in the thread’s `local_copy` and the shared `database.value`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The diagram is laid out so that time increases as you move from top to bottom. It begins when `Thread 1` is created and ends when it is terminated.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When `Thread 1` starts, `FakeDatabase.value` is zero. The first line of code in the method, `local_copy = self.value`, copies the value zero to the local variable. Next it increments the value of `local_copy` with the `local_copy += 1` statement. You can see `.value` in `Thread 1` getting set to one.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next `time.sleep()` is called, which makes the current thread pause and allows other threads to run. Since there is only one thread in this example, this has no effect.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When `Thread 1` wakes up and continues, it copies the new value from `local_copy` to `FakeDatabase.value`, and then the thread is complete. You can see that `database.value` is set to one.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So far, so good. You ran `.update()` once and `FakeDatabase.value` was incremented to one.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Two Threads [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Getting back to the race condition, the two threads will be running concurrently but not at the same time. They will each have their own version of `local_copy` and will each point to the same `database`. It is this shared `database` object that is going to cause the problems.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The program starts with `Thread 1` running `.update()`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When `Thread 1` calls `time.sleep()`, it allows the other thread to start running. This is where things get interesting.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`Thread 2` starts up and does the same operations. It’s also copying `database.value` into its private `local_copy`, and this shared `database.value` has not yet been updated:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When `Thread 2` finally goes to sleep, the shared `database.value` is still unmodified at zero, and both private versions of `local_copy` have the value one.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`Thread 1` now wakes up and saves its version of `local_copy` and then terminates, giving `Thread 2` a final chance to run. `Thread 2` has no idea that `Thread 1` ran and updated `database.value` while it was sleeping. It stores *its* version of `local_copy` into `database.value`, also setting it to one:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The two threads have interleaving access to a single shared object, overwriting each other’s results. Similar race conditions can arise when one thread frees memory or closes a file handle before the other thread is finished accessing it.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Why This Isn’t a Silly Example [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The example above is contrived to make sure that the race condition happens every time you run your program. Because the operating system can swap out a thread at any time, it is possible to interrupt a statement like `x = x + 1` after it has read the value of `x` but before it has written back the incremented value.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The details of how this happens are quite interesting, but not needed for the rest of this article, so feel free to skip over the section below.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "#### How Does This Really Work [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The code above isn’t quite as out there as you might originally have thought. It was designed to force a race condition every time you run it, but that makes it much easier to solve than most race conditions."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are two things to keep in mind when thinking about race conditions:\n",
+ "\n",
+ "1. Even an operation like x += 1 takes the processor many steps. Each of these steps is a separate instruction to the processor.\n",
+ "2. The operating system can swap which thread is running at any time. A thread can be swapped out after any of these small instructions. This means that a thread can be put to sleep to let another thread run in the middle of a Python statement."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s look at this in detail. The REPL below shows a function that takes a parameter and increments it:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def inc(x):\n",
+ " x += 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import dis"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 2 0 LOAD_FAST 0 (x)\n",
+ " 2 LOAD_CONST 1 (1)\n",
+ " 4 INPLACE_ADD\n",
+ " 6 STORE_FAST 0 (x)\n",
+ " 8 LOAD_CONST 0 (None)\n",
+ " 10 RETURN_VALUE\n"
+ ]
+ }
+ ],
+ "source": [
+ "dis.dis(inc)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The REPL example uses [dis](https://docs.python.org/3/library/dis.html) from the Python standard library to show the smaller steps that the processor does to implement your function. It does a `LOAD_FAST` of the data value `x`, it does a `LOAD_CONST 1`, and then it uses the `INPLACE_ADD` to add those values together."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We’re stopping here for a specific reason. This is the point in `.update()` above where `time.sleep()` forced the threads to switch. It is entirely possible that, every once in while, the operating system would switch threads at that exact point even without `sleep()`, but the call to `sleep()` makes it happen every time."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you learned above, the operating system can swap threads at any time. You’ve walked down this listing to the statement marked 4. If the operating system swaps out this thread and runs a different thread that also modifies `x`, then when this thread resumes, it will overwrite `x` with an incorrect value."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Technically, this example won’t have a race condition because `x` is local to `inc()`. It does illustrate how a thread can be interrupted during a single Python operation, however. The same `LOAD`, `MODIFY`, `STORE` set of operations also happens on global and shared values. You can explore with the `dis` module and prove that yourself."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It’s rare to get a race condition like this to occur, but remember that an infrequent event taken over millions of iterations becomes likely to happen. **The rarity of these race conditions makes them much, much harder to debug than regular bugs.**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now back to your regularly scheduled tutorial!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now that you’ve seen a race condition in action, let’s find out how to solve them!\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Basic Synchronization Using Lock [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are a number of ways to avoid or solve race conditions. You won’t look at all of them here, but there are a couple that are used frequently. Let’s start with `Lock`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To solve your race condition above, you need to find a way to allow only one thread at a time into the read-modify-write section of your code. The most common way to do this is called `Lock` in Python. In some other languages this same idea is called a `mutex`. Mutex comes from MUTual EXclusion, which is exactly what a `Lock` does.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A `Lock` is an object that acts like a hall pass. Only one thread at a time can have the `Lock`. Any other thread that wants the `Lock` must wait until the owner of the `Lock` gives it up.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The basic functions to do this are `.acquire()` and `.release()`. A thread will call `my_lock.acquire()` to get the lock. If the lock is already held, the calling thread will wait until it is released. There’s an important point here. If one thread gets the lock but never gives it back, your program will be stuck. You’ll read more about this later.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Fortunately, Python’s `Lock` will also operate as a context manager, so you can use it in a `with` statement, and it gets released automatically when the `with` block exits for any reason.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s look at the `FakeDatabase` with a `Lock` added to it. The calling function stays the same:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class FakeDatabase:\n",
+ " def __init__(self):\n",
+ " self.value = 0\n",
+ " self._lock = threading.Lock()\n",
+ "\n",
+ " def locked_update(self, name):\n",
+ " logging.info(\"Thread %s: starting update\", name)\n",
+ " logging.debug(\"Thread %s about to lock\", name)\n",
+ " with self._lock:\n",
+ " logging.debug(\"Thread %s has lock\", name)\n",
+ " local_copy = self.value\n",
+ " local_copy += 1\n",
+ " time.sleep(0.1)\n",
+ " self.value = local_copy\n",
+ " logging.debug(\"Thread %s about to release lock\", name)\n",
+ " logging.debug(\"Thread %s after release\", name)\n",
+ " logging.info(\"Thread %s: finishing update\", name)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Other than adding a bunch of debug logging so you can see the locking more clearly, the big change here is to add a member called `._lock`, which is a `threading.Lock()` object. This `._lock` is initialized in the unlocked state and locked and released by the `with` statement.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It’s worth noting here that the thread running this function will hold on to that `Lock` until it is completely finished updating the database. In this case, that means it will hold the `Lock` while it copies, updates, sleeps, and then writes the value back to the database.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you run this version with logging set to warning level, you’ll see this:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./fixrace.py\n",
+ "Testing locked update. Starting value is 0.\n",
+ "Thread 0: starting update\n",
+ "Thread 1: starting update\n",
+ "Thread 0: finishing update\n",
+ "Thread 1: finishing update\n",
+ "Testing locked update. Ending value is 2.\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Look at that. Your program finally works!\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can turn on full logging by setting the level to `DEBUG` by adding this statement after you configure the logging output in `__main__`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "logging.getLogger().setLevel(logging.DEBUG)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running this program with `DEBUG` logging turned on looks like this:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./fixrace.py\n",
+ "Testing locked update. Starting value is 0.\n",
+ "Thread 0: starting update\n",
+ "Thread 0 about to lock\n",
+ "Thread 0 has lock\n",
+ "Thread 1: starting update\n",
+ "Thread 1 about to lock\n",
+ "Thread 0 about to release lock\n",
+ "Thread 0 after release\n",
+ "Thread 0: finishing update\n",
+ "Thread 1 has lock\n",
+ "Thread 1 about to release lock\n",
+ "Thread 1 after release\n",
+ "Thread 1: finishing update\n",
+ "Testing locked update. Ending value is 2.\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this output you can see `Thread 0` acquires the lock and is still holding it when it goes to sleep. `Thread 1` then starts and attempts to acquire the same lock. Because `Thread 0` is still holding it, `Thread 1` has to wait. This is the mutual exclusion that a `Lock` provides.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Many of the examples in the rest of this article will have `WARNING` and `DEBUG` level logging. We’ll generally only show the `WARNING` level output, as the `DEBUG` logs can be quite lengthy. Try out the programs with the logging turned up and see what they do.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Deadlock [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before you move on, you should look at a common problem when using `Locks`. As you saw, if the `Lock` has already been acquired, a second call to `.acquire()` will wait until the thread that is holding the `Lock` calls `.release()`. What do you think happens when you run this code:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import threading\n",
+ "\n",
+ "l = threading.Lock()\n",
+ "print(\"before first acquire\")\n",
+ "l.acquire()\n",
+ "print(\"before second acquire\")\n",
+ "l.acquire()\n",
+ "print(\"acquired lock twice\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When the program calls `l.acquire()` the second time, it hangs waiting for the `Lock` to be released. In this example, you can fix the deadlock by removing the second call, but deadlocks usually happen from one of two subtle things:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "1. An implementation bug where a `Lock` is not released properly\n",
+ "2. A design issue where a utility function needs to be called by functions that might or might not already have the `Lock`\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The first situation happens sometimes, but using a `Lock` as a context manager greatly reduces how often. It is recommended to write code whenever possible to make use of context managers, as they help to avoid situations where an exception skips you over the `.release()` call.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The design issue can be a bit trickier in some languages. Thankfully, Python threading has a second object, called `RLock`, that is designed for just this situation. It allows a thread to `.acquire()` an `RLock` multiple times before it calls `.release()`. That thread is still required to call `.release()` the same number of times it called `.acquire()`, but it should be doing that anyway.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`Lock` and `RLock` are two of the basic tools used in threaded programming to prevent race conditions. There are a few other that work in different ways. Before you look at them, let’s shift to a slightly different problem domain.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Threading Objects [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are a few more primitives offered by the Python `threading` module. While you didn’t need these for the examples above, they can come in handy in different use cases, so it’s good to be familiar with them.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Semaphore [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The first Python `threading` object to look at is `threading.Semaphore`. A `Semaphore` is a counter with a few special properties. The first one is that the counting is atomic. This means that there is a guarantee that the operating system will not swap out the thread in the middle of incrementing or decrementing the counter.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The internal counter is incremented when you call `.release()` and decremented when you call `.acquire()`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The next special property is that if a thread calls `.acquire()` when the counter is zero, that thread will block until a different thread calls `.release()` and increments the counter to one.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Semaphores are frequently used to protect a resource that has a limited capacity. An example would be if you have a pool of connections and want to limit the size of that pool to a specific number.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Timer [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A `threading.Timer` is a way to schedule a function to be called after a certain amount of time has passed. You create a `Timer` by passing in a number of seconds to wait and a function to call:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "t = threading.Timer(30.0, my_function)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You start the `Timer` by calling `.start()`. The function will be called on a new thread at some point after the specified time, but be aware that there is no promise that it will be called exactly at the time you want.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you want to stop a `Timer` that you’ve already started, you can cancel it by calling `.cancel()`. Calling `.cancel()` after the `Timer` has triggered does nothing and does not produce an exception.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A `Timer` can be used to prompt a user for action after a specific amount of time. If the user does the action before the `Timer` expires, `.cancel()` can be called.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Barrier [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A `threading.Barrier` can be used to keep a fixed number of threads in sync. When creating a `Barrier`, the caller must specify how many threads will be synchronizing on it. Each thread calls `.wait()` on the `Barrier`. They all will remain blocked until the specified number of threads are waiting, and then the are all released at the same time.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Remember that threads are scheduled by the operating system so, even though all of the threads are released simultaneously, they will be scheduled to run one at a time.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "One use for a `Barrier` is to allow a pool of threads to initialize themselves. Having the threads wait on a `Barrier` after they are initialized will ensure that none of the threads start running before all of the threads are finished with their initialization.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Download Sites: Threading Version [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you probably guessed, writing a threaded program takes more effort. You might be surprised at how little extra effort it takes for simple cases, however. Here’s what the same program looks like with `threading`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import concurrent.futures\n",
+ "import requests\n",
+ "import threading\n",
+ "import time"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "thread_local = threading.local()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_session():\n",
+ " if not hasattr(thread_local, \"session\"):\n",
+ " thread_local.session = requests.Session()\n",
+ " return thread_local.session"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def download_site(url):\n",
+ " session = get_session()\n",
+ " with session.get(url) as response:\n",
+ " print(f\"Read {len(response.content)} from {url}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def download_all_sites(sites):\n",
+ " with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:\n",
+ " executor.map(download_site, sites)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sites = [\n",
+ " \"https://www.jython.org\",\n",
+ " \"http://olympus.realpython.org/dice\",\n",
+ "] * 80\n",
+ "start_time = time.time()\n",
+ "download_all_sites(sites)\n",
+ "duration = time.time() - start_time\n",
+ "print(f\"Downloaded {len(sites)} in {duration} seconds\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you add `threading`, the overall structure is the same and you only needed to make a few changes. `download_all_sites()` changed from calling the function once per site to a more complex structure.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In this version, you’re creating a `ThreadPoolExecutor`, which seems like a complicated thing. Let’s break that down: `ThreadPoolExecutor` = `Thread` + `Pool` + `Executor`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You already know about the `Thread` part. That’s just a train of thought we mentioned earlier. The `Pool` portion is where it starts to get interesting. This object is going to create a pool of threads, each of which can run concurrently. Finally, the `Executor` is the part that’s going to control how and when each of the threads in the pool will run. It will execute the request in the pool.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Helpfully, the standard library implements `ThreadPoolExecutor` as a context manager so you can use the `with` syntax to manage creating and freeing the pool of `Threads`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Once you have a `ThreadPoolExecutor`, you can use its handy `.map()` method. This method runs the passed-in function on each of the sites in the list. The great part is that it automatically runs them concurrently using the pool of threads it is managing.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Those of you coming from other languages, or even Python 2, are probably wondering where the usual objects and functions are that manage the details you’re used to when dealing with `threading`, things like `Thread.start()`, `Thread.join()`, and `Queue`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "These are all still there, and you can use them to achieve fine-grained control of how your threads are run. But, starting with Python 3.2, the standard library added a higher-level abstraction called `Executors` that manage many of the details for you if you don’t need that fine-grained control.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The other interesting change in our example is that each thread needs to create its own `requests.Session()` object. When you’re looking at the documentation for `requests`, it’s not necessarily easy to tell, but reading [this issue](https://github.com/requests/requests/issues/2766), it seems fairly clear that you need a separate Session for each thread.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This is one of the interesting and difficult issues with `threading`. Because the operating system is in control of when your task gets interrupted and another task starts, any data that is shared between the threads needs to be protected, or thread-safe. Unfortunately `requests.Session()` is not thread-safe.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "There are several strategies for making data accesses thread-safe depending on what the data is and how you’re using it. One of them is to use thread-safe data structures like `Queue` from Python’s `queue` module.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "These objects use low-level primitives like [`threading.Lock`](https://docs.python.org/2/library/threading.html#lock-objects) to ensure that only one thread can access a block of code or a bit of memory at the same time. You are using this strategy indirectly by way of the `ThreadPoolExecutor` object.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Another strategy to use here is something called thread local storage. `threading.local()` creates an object that looks like a global but is specific to each individual thread. In your example, this is done with `thread_local` and `get_session()`:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "thread_local = threading.local()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_session():\n",
+ " if not hasattr(thread_local, \"session\"):\n",
+ " thread_local.session = requests.Session()\n",
+ " return thread_local.session"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`local()` is in the `threading` module to specifically solve this problem. It looks a little odd, but you only want to create one of these objects, not one for each thread. The object itself takes care of separating accesses from different threads to different data.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When `get_session()` is called, the `session` it looks up is specific to the particular thread on which it’s running. So each thread will create a single session the first time it calls `get_session()` and then will simply use that session on each subsequent call throughout its lifetime.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, a quick note about picking the number of threads. You can see that the example code uses 5 threads. Feel free to play around with this number and see how the overall time changes. You might expect that having one thread per download would be the fastest but, at least on my system it was not. I found the fastest results somewhere between 5 and 10 threads. If you go any higher than that, then the extra overhead of creating and destroying the threads erases any time savings.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The difficult answer here is that the correct number of threads is not a constant from one task to another. Some experimentation is required.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Why the `threading` Version Rocks**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It’s fast! Here’s the fastest run of my tests. Remember that the non-concurrent version took more than 14 seconds:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ ./io_threading.py\n",
+ " [most output skipped]\n",
+ "Downloaded 160 in 3.7238826751708984 seconds\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here’s what its execution timing diagram looks like:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "It uses multiple threads to have multiple open requests out to web sites at the same time, allowing your program to overlap the waiting times and get the final result faster! Yippee! That was the goal.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**The Problems with the `threading` Version**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Well, as you can see from the example, it takes a little more code to make this happen, and you really have to give some thought to what data is shared between threads.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Threads can interact in ways that are subtle and hard to detect. These interactions can cause race conditions that frequently result in random, intermittent bugs that can be quite difficult to find.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Conclusion: Threading in Python [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You’ve now seen much of what Python `threading` has to offer and some examples of how to build threaded programs and the problems they solve. You’ve also seen a few instances of the problems that arise when writing and debugging threaded programs.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you’d like to explore other options for concurrency in Python, check out [Speed Up Your Python Program With Concurrency](https://realpython.com/python-concurrency/)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you’re interested in doing a deep dive on the `asyncio` module, go read [Async IO in Python: A Complete Walkthrough](https://realpython.com/async-io-python/).\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Whatever you do, you now have the information and confidence you need to write programs using Python threading!\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "*Special thanks to reader JL Diaz for helping to clean up the introduction.*\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.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/Python/04. Advanced/07.3 What Is the Python Global Interpreter Lock (GIL)?.ipynb b/Python/04. Advanced/07.3 What Is the Python Global Interpreter Lock (GIL)?.ipynb
new file mode 100644
index 0000000..9b848b0
--- /dev/null
+++ b/Python/04. Advanced/07.3 What Is the Python Global Interpreter Lock (GIL)?.ipynb
@@ -0,0 +1,681 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
What Is the Python Global Interpreter Lock (GIL)? \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "##
Table of Contents\n",
+ "* [What Problem Did the GIL Solve for Python?](#what_problem_did_the_gil_solve_for_python?)\n",
+ "* [Why Was the GIL Chosen as the Solution?](#why_was_the_gil_chosen_as_the_solution?)\n",
+ "* [The Impact on Multi-Threaded Python Programs](#the_impact_on_multi-threaded_python_programs)\n",
+ "* [Why Hasn’t the GIL Been Removed Yet?](#why_hasn’t_the_gil_been_removed_yet?)\n",
+ "* [Why Wasn’t It Removed in Python 3?](#why_wasn’t_it_removed_in_python_3?)\n",
+ "* [How to Deal With Python’s GIL](#how_to_deal_with_python’s_gil)\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Python Global Interpreter Lock or [GIL](https://wiki.python.org/moin/GlobalInterpreterLock), in simple words, is a mutex (or a lock) that allows only one [thread](https://realpython.com/intro-to-python-threading/) to hold the control of the Python interpreter. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This means that only one thread can be in a state of execution at any point in time. The impact of the GIL isn’t visible to developers who execute single-threaded programs, but it can be a performance bottleneck in CPU-bound and multi-threaded code.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Since the GIL allows only one thread to execute at a time even in a multi-threaded architecture with more than one CPU core, the GIL has gained a reputation as an “infamous” feature of Python.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**In this article you’ll learn how the GIL affects the performance of your Python programs, and how you can mitigate the impact it might have on your code.**\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## What Problem Did the GIL Solve for Python? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python uses reference counting for [memory management](https://realpython.com/python-memory-management/). It means that objects created in Python have a reference count variable that keeps track of the number of references that point to the object. When this count reaches zero, the memory occupied by the object is released.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s take a look at a brief code example to demonstrate how reference counting works:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ ">>>>>> import sys\n",
+ ">>> a = []\n",
+ ">>> b = a\n",
+ ">>> sys.getrefcount(a)\n",
+ "3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the above example, the reference count for the empty list object `[]` was 3. The list object was referenced by `a`, `b` and the argument passed to `sys.getrefcount()`.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Back to the GIL:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The problem was that this reference count variable needed protection from race conditions where two threads increase or decrease its value simultaneously. If this happens, it can cause either leaked memory that is never released or, even worse, incorrectly release the memory while a reference to that object still exists. This can cause crashes or other “weird” bugs in your Python programs.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This reference count variable can be kept safe by adding *locks* to all data structures that are shared across threads so that they are not modified inconsistently. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But adding a lock to each object or groups of objects means multiple locks will exist which can cause another problem—Deadlocks (deadlocks can only happen if there is more than one lock). Another side effect would be decreased performance caused by the repeated acquisition and release of locks.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The GIL is a single lock on the interpreter itself which adds a rule that execution of any Python bytecode requires acquiring the interpreter lock. This prevents deadlocks (as there is only one lock) and doesn’t introduce much performance overhead. But it effectively makes any CPU-bound Python program single-threaded.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The GIL, although used by interpreters for other languages like Ruby, is not the only solution to this problem. Some languages avoid the requirement of a GIL for thread-safe memory management by using approaches other than reference counting, such as garbage collection. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "On the other hand, this means that those languages often have to compensate for the loss of single threaded performance benefits of a GIL by adding other performance boosting features like JIT compilers.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## Why Was the GIL Chosen as the Solution? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "So, why was an approach that is seemingly so obstructing used in Python? Was it a bad decision by the developers of Python? \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Well, in the [words of Larry Hastings](https://youtu.be/KVKufdTphKs?t=12m11s), the design decision of the GIL is one of the things that made Python as popular as it is today.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python has been around since the days when operating systems did not have a concept of threads. Python was designed to be easy-to-use in order to make development quicker and more and more developers started using it. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A lot of extensions were being written for the existing C libraries whose features were needed in Python. To prevent inconsistent changes, these C extensions required a thread-safe memory management which the GIL provided.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The GIL is simple to implement and was easily added to Python. It provides a performance increase to single-threaded programs as only one lock needs to be managed.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "C libraries that were not thread-safe became easier to integrate. And these C extensions became one of the reasons why Python was readily adopted by different communities.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you can see, the GIL was a pragmatic solution to a difficult problem that the [CPython](https://realpython.com/cpython-source-code-guide/) developers faced early on in Python’s life.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## The Impact on Multi-Threaded Python Programs [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When you look at a typical Python program—or any computer program for that matter—there’s a difference between those that are CPU-bound in their performance and those that are I/O-bound.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "CPU-bound programs are those that are pushing the CPU to its limit. This includes programs that do mathematical computations like matrix multiplications, searching, image processing, etc. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "I/O-bound programs are the ones that spend time waiting for [Input/Output](https://realpython.com/python-input-output/) which can come from a user, file, database, network, etc. I/O-bound programs sometimes have to wait for a significant amount of time till they get what they need from the source due to the fact that the source may need to do its own processing before the input/output is ready, for example, a user thinking about what to enter into an input prompt or a database query running in its own process.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let’s have a look at a simple CPU-bound program that performs a countdown:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# single_threaded.py\n",
+ "import time\n",
+ "from threading import Thread\n",
+ "\n",
+ "COUNT = 50000000\n",
+ "\n",
+ "def countdown(n):\n",
+ " while n>0:\n",
+ " n -= 1\n",
+ "\n",
+ "start = time.time()\n",
+ "countdown(COUNT)\n",
+ "end = time.time()\n",
+ "\n",
+ "print('Time taken in seconds -', end - start)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running this code on my system with 4 cores gave the following output:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ python single_threaded.py\n",
+ "Time taken in seconds - 6.20024037361145\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now I modified the code a bit to do to the same countdown using two threads in parallel:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# multi_threaded.py\n",
+ "import time\n",
+ "from threading import Thread\n",
+ "\n",
+ "COUNT = 50000000\n",
+ "\n",
+ "def countdown(n):\n",
+ " while n>0:\n",
+ " n -= 1\n",
+ "\n",
+ "t1 = Thread(target=countdown, args=(COUNT//2,))\n",
+ "t2 = Thread(target=countdown, args=(COUNT//2,))\n",
+ "\n",
+ "start = time.time()\n",
+ "t1.start()\n",
+ "t2.start()\n",
+ "t1.join()\n",
+ "t2.join()\n",
+ "end = time.time()\n",
+ "\n",
+ "print('Time taken in seconds -', end - start)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "And when I ran it again:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ python multi_threaded.py\n",
+ "Time taken in seconds - 6.924342632293701\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you can see, both versions take almost same amount of time to finish. In the multi-threaded version the GIL prevented the CPU-bound threads from executing in parellel.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The GIL does not have much impact on the performance of I/O-bound multi-threaded programs as the lock is shared between threads while they are waiting for I/O. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But a program whose threads are entirely CPU-bound, e.g., a program that processes an image in parts using threads, would not only become single threaded due to the lock but will also see an increase in execution time, as seen in the above example, in comparison to a scenario where it was written to be entirely single-threaded. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This increase is the result of acquire and release overheads added by the lock.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## Why Hasn’t the GIL Been Removed Yet? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The developers of Python receive a lot of complaints regarding this but a language as popular as Python cannot bring a change as significant as the removal of GIL without causing backward incompatibility issues. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The GIL can obviously be removed and this has been done multiple times in the past by the developers and researchers but all those attempts broke the existing C extensions which depend heavily on the solution that the GIL provides.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Of course, there are other solutions to the problem that the GIL solves but some of them decrease the performance of single-threaded and multi-threaded I/O-bound programs and some of them are just too difficult. After all, you wouldn’t want your existing Python programs to run slower after a new version comes out, right?\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The creator and BDFL of Python, Guido van Rossum, gave an answer to the community in September 2007 in his article [“It isn’t Easy to remove the GIL”](https://www.artima.com/weblogs/viewpost.jsp?thread=214235):\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "And this condition hasn’t been fulfilled by any of the attempts made since.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## Why Wasn’t It Removed in Python 3? [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python 3 did have a chance to start a lot of features from scratch and in the process, broke some of the existing C extensions which then required changes to be updated and ported to work with Python 3. This was the reason why the early versions of Python 3 saw slower adoption by the community. \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But why wasn’t GIL removed alongside? \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Removing the GIL would have made Python 3 slower in comparison to Python 2 in single-threaded performance and you can imagine what that would have resulted in. You can’t argue with the single-threaded performance benefits of the GIL. So the result is that Python 3 still has the GIL.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But Python 3 did bring a major improvement to the existing GIL—\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We discussed the impact of GIL on “only CPU-bound” and “only I/O-bound” multi-threaded programs but what about the programs where some threads are I/O-bound and some are CPU-bound? \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In such programs, Python’s GIL was known to starve the I/O-bound threads by not giving them a chance to acquire the GIL from CPU-bound threads.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This was because of a mechanism built into Python that forced threads to release the GIL **after a fixed interval** of continuous use and if nobody else acquired the GIL, the same thread could continue its use.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ ">>> import sys\n",
+ ">>> # The interval is set to 100 instructions:\n",
+ ">>> sys.getcheckinterval()\n",
+ "100"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The problem in this mechanism was that most of the time the CPU-bound thread would reacquire the GIL itself before other threads could acquire it. This was researched by David Beazley and visualizations can be found [here](http://www.dabeaz.com/blog/2010/01/python-gil-visualized.html).\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This problem was fixed in Python 3.2 in 2009 by Antoine Pitrou who [added a mechanism](https://mail.python.org/pipermail/python-dev/2009-October/093321.html) of looking at the number of GIL acquisition requests by other threads that got dropped and not allowing the current thread to reacquire GIL before other threads got a chance to run.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "## How to Deal With Python’s GIL [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If the GIL is causing you problems, here a few approaches you can try:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Multi-processing vs multi-threading:** The most popular way is to use a multi-processing approach where you use multiple processes instead of threads. Each Python process gets its own Python interpreter and memory space so the GIL won’t be a problem. Python has a [`multiprocessing`](https://docs.python.org/2/library/multiprocessing.html) module which lets us create processes easily like this:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from multiprocessing import Pool\n",
+ "import time\n",
+ "\n",
+ "COUNT = 50000000\n",
+ "def countdown(n):\n",
+ " while n>0:\n",
+ " n -= 1\n",
+ "\n",
+ "if __name__ == '__main__':\n",
+ " pool = Pool(processes=2)\n",
+ " start = time.time()\n",
+ " r1 = pool.apply_async(countdown, [COUNT//2])\n",
+ " r2 = pool.apply_async(countdown, [COUNT//2])\n",
+ " pool.close()\n",
+ " pool.join()\n",
+ " end = time.time()\n",
+ " print('Time taken in seconds -', end - start)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running this on my system gave this output:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```sh\n",
+ "$ python multiprocess.py\n",
+ "Time taken in seconds - 4.060242414474487\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A decent performance increase compared to the multi-threaded version, right? \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The time didn’t drop to half of what we saw above because process management has its own overheads. Multiple processes are heavier than multiple threads, so, keep in mind that this could become a scaling bottleneck.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Alternative Python interpreters:** Python has multiple interpreter implementations. CPython, Jython, IronPython and [PyPy](https://realpython.com/pypy-faster-python/), written in [C](https://realpython.com/c-for-python-programmers/), [Java](https://realpython.com/oop-in-python-vs-java/), C# and Python respectively, are the most popular ones. GIL exists only in the original Python implementation that is CPython. If your program, with its libraries, is available for one of the other implementations then you can try them out as well.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Just wait it out:** While many Python users take advantage of the single-threaded performance benefits of GIL. The multi-threading programmers don’t have to fret as some of the brightest minds in the Python community are working to remove the GIL from CPython. One such attempt is known as the [Gilectomy](https://github.com/larryhastings/gilectomy). \n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Python GIL is often regarded as a mysterious and difficult topic. But keep in mind that as a Pythonista you’re usually only affected by it if you are writing C extensions or if you’re using CPU-bound multi-threading in your programs.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In that case, this article should give you everything you need to understand what the GIL is and how to deal with it in your own projects. And if you want to understand the low-level inner workings of GIL, I’d recommend you watch the [Understanding the Python GIL](https://youtu.be/Obt-vMVdM8s) talk by David Beazley.\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.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-shared-database.267a5d8c6aa1.png b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-shared-database.267a5d8c6aa1.png
new file mode 100644
index 0000000..b2bf7ca
Binary files /dev/null and b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-shared-database.267a5d8c6aa1.png differ
diff --git a/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-single-thread.6a11288bc199.png b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-single-thread.6a11288bc199.png
new file mode 100644
index 0000000..0ea024c
Binary files /dev/null and b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-single-thread.6a11288bc199.png differ
diff --git a/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part1.c1c0e65a8481.png b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part1.c1c0e65a8481.png
new file mode 100644
index 0000000..17f7a63
Binary files /dev/null and b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part1.c1c0e65a8481.png differ
diff --git a/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part2.df42d4fbfe21.png b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part2.df42d4fbfe21.png
new file mode 100644
index 0000000..4d4cc9c
Binary files /dev/null and b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part2.df42d4fbfe21.png differ
diff --git a/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part3.18576920f88f.png b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part3.18576920f88f.png
new file mode 100644
index 0000000..a43dde8
Binary files /dev/null and b/Python/04. Advanced/images/an-intro-to-threading-in-python/intro-threading-two-threads-part3.18576920f88f.png differ
diff --git a/Python/04. Advanced/images/async-io-in-python:-a-complete-walkthrough/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg b/Python/04. Advanced/images/async-io-in-python:-a-complete-walkthrough/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg
new file mode 100644
index 0000000..b57d3b6
Binary files /dev/null and b/Python/04. Advanced/images/async-io-in-python:-a-complete-walkthrough/Screen_Shot_2018-10-17_at_3.18.44_PM.c02792872031.jpg differ
diff --git a/Python/04. Advanced/images/async-io-in-python:-a-complete-walkthrough/asyncio-rand.dffdd83b4256.gif b/Python/04. Advanced/images/async-io-in-python:-a-complete-walkthrough/asyncio-rand.dffdd83b4256.gif
new file mode 100644
index 0000000..e0431ea
Binary files /dev/null and b/Python/04. Advanced/images/async-io-in-python:-a-complete-walkthrough/asyncio-rand.dffdd83b4256.gif differ
diff --git a/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/Asyncio.31182d3731cf.png b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/Asyncio.31182d3731cf.png
new file mode 100644
index 0000000..2821742
Binary files /dev/null and b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/Asyncio.31182d3731cf.png differ
diff --git a/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/CPUBound.d2d32cb2626c.png b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/CPUBound.d2d32cb2626c.png
new file mode 100644
index 0000000..9a8f772
Binary files /dev/null and b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/CPUBound.d2d32cb2626c.png differ
diff --git a/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/CPUMP.69c1a7fad9c4.png b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/CPUMP.69c1a7fad9c4.png
new file mode 100644
index 0000000..0074d4e
Binary files /dev/null and b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/CPUMP.69c1a7fad9c4.png differ
diff --git a/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/IOBound.4810a888b457.png b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/IOBound.4810a888b457.png
new file mode 100644
index 0000000..109315f
Binary files /dev/null and b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/IOBound.4810a888b457.png differ
diff --git a/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/MProc.7cf3be371bbc.png b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/MProc.7cf3be371bbc.png
new file mode 100644
index 0000000..9418868
Binary files /dev/null and b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/MProc.7cf3be371bbc.png differ
diff --git a/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/Threading.3eef48da829e.png b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/Threading.3eef48da829e.png
new file mode 100644
index 0000000..c8939be
Binary files /dev/null and b/Python/04. Advanced/images/speed-up-your-python-program-with-concurrency/Threading.3eef48da829e.png differ
diff --git a/python/05. Modules/14.1 json.ipynb b/Python/05. Modules/01.1 json.ipynb
similarity index 84%
rename from python/05. Modules/14.1 json.ipynb
rename to Python/05. Modules/01.1 json.ipynb
index 9d2b636..e9905bf 100755
--- a/python/05. Modules/14.1 json.ipynb
+++ b/Python/05. Modules/01.1 json.ipynb
@@ -2,15 +2,45 @@
"cells": [
{
"cell_type": "markdown",
- "id": "complicated-punishment",
"metadata": {},
"source": [
- "# Working With JSON Data in Python"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Working With JSON Data in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [A (Very) Brief History of JSON](#a_(very)_brief_history_of_json)\n",
+ "* [Look, it’s JSON!](#look,_it’s_json!)\n",
+ "* [Python Supports JSON Natively!](#python_supports_json_natively!)\n",
+ "* [A Little Vocabulary](#a_little_vocabulary)\n",
+ "* [Serializing JSON](#serializing_json)\n",
+ "* [A Simple Serialization Example](#a_simple_serialization_example)\n",
+ "* [Some Useful Keyword Arguments](#some_useful_keyword_arguments)\n",
+ "* [Deserializing JSON](#deserializing_json)\n",
+ " * [A Simple Deserialization Example](#a_simple_deserialization_example)\n",
+ "* [Encoding and Decoding Custom Python Objects](#encoding_and_decoding_custom_python_objects)\n",
+ " * [Simplifying Data Structures](#simplifying_data_structures)\n",
+ " * [Encoding Custom Types](#encoding_custom_types)\n",
+ " * [Decoding Custom Types](#decoding_custom_types)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "plain-perfume",
"metadata": {},
"source": [
"Since its inception, [JSON](https://en.wikipedia.org/wiki/JSON) has quickly become the de facto standard for information exchange. Chances are you’re here because you need to transport some data from here to there. Perhaps you’re gathering information through an [API](https://realpython.com/api-integration-in-python/) or storing your data in a [document database](https://realpython.com/introduction-to-mongodb-and-python/). One way or another, you’re up to your neck in JSON, and you’ve got to Python your way out."
@@ -18,7 +48,6 @@
},
{
"cell_type": "markdown",
- "id": "accomplished-right",
"metadata": {},
"source": [
"Luckily, this is a pretty common task, and—as with most common tasks—Python makes it almost disgustingly easy. Have no fear, fellow Pythoneers and Pythonistas. This one’s gonna be a breeze!"
@@ -26,7 +55,6 @@
},
{
"cell_type": "markdown",
- "id": "geographic-tourism",
"metadata": {},
"source": [
"**So, we use JSON to store and exchange data?** Yup, you got it! It’s nothing more than a standardized format the community uses to pass data around. Keep in mind, JSON isn’t the only format available for this kind of work, but [XML](https://en.wikipedia.org/wiki/XML) and [YAML](http://yaml.org/) are probably the only other ones worth mentioning in the same breath."
@@ -34,15 +62,14 @@
},
{
"cell_type": "markdown",
- "id": "organic-stability",
"metadata": {},
"source": [
- "## A (Very) Brief History of JSON"
+ "\n",
+ "## A (Very) Brief History of JSON [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "checked-greece",
"metadata": {},
"source": [
"Not so surprisingly, **J**ava**S**cript **O**bject **N**otation was inspired by a subset of the JavaScript programming language dealing with object literal syntax. They’ve got a [nifty website](https://www.json.org/) that explains the whole thing. Don’t worry though: JSON has long since become language agnostic and exists as its own standard, so we can thankfully avoid JavaScript for the sake of this discussion.\n",
@@ -52,15 +79,14 @@
},
{
"cell_type": "markdown",
- "id": "provincial-token",
"metadata": {},
"source": [
- "## Look, it’s JSON!"
+ "\n",
+ "## Look, it’s JSON! [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "historical-spencer",
"metadata": {},
"source": [
"Get ready. I’m about to show you some real life JSON—just like you’d see out there in the wild. It’s okay: JSON is supposed to be readable by anyone who’s used a C-style language, and Python is a C-style language…so that’s you!\n",
@@ -87,7 +113,6 @@
},
{
"cell_type": "markdown",
- "id": "civilian-tiffany",
"metadata": {},
"source": [
"**Wait, that looks like a Python dictionary!** I know, right? It’s pretty much universal object notation at this point, but I don’t think UON rolls off the tongue quite as nicely. Feel free to discuss alternatives in the comments."
@@ -95,15 +120,14 @@
},
{
"cell_type": "markdown",
- "id": "graphic-efficiency",
"metadata": {},
"source": [
- "## Python Supports JSON Natively!"
+ "\n",
+ "## Python Supports JSON Natively! [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "equal-rogers",
"metadata": {},
"source": [
"Python comes with a built-in package called [json](https://docs.python.org/3/library/json.html) for encoding and decoding JSON data.\n",
@@ -117,17 +141,16 @@
},
{
"cell_type": "markdown",
- "id": "daily-style",
"metadata": {},
"source": [
- "## A Little Vocabulary\n",
+ "\n",
+ "## A Little Vocabulary [
](#table_of_contents)\n",
"\n",
"The process of encoding JSON is usually called **serialization**. This term refers to the transformation of data into a series of bytes (hence serial) to be stored or transmitted across a network. You may also hear the term **marshaling**, but that’s a whole other discussion. Naturally, **deserialization** is the reciprocal process of decoding data that has been stored or delivered in the JSON standard."
]
},
{
"cell_type": "markdown",
- "id": "communist-flash",
"metadata": {},
"source": [
"**Yikes! That sounds pretty technical**. Definitely. But in reality, all we’re talking about here is _reading_ and _writing_. Think of it like this: _encoding_ is for _writing_ data to disk, while _decoding_ is for _reading_ data into memory."
@@ -135,15 +158,14 @@
},
{
"cell_type": "markdown",
- "id": "outside-chicago",
"metadata": {},
"source": [
- "## Serializing JSON"
+ "\n",
+ "## Serializing JSON [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "entitled-massage",
"metadata": {},
"source": [
"What happens after a computer processes lots of information? It needs to take a data dump. Accordingly, the `json` library exposes the `dump()` method for writing data to files. There is also a `dumps()` method (pronounced as “dump-s”) for writing to a Python string."
@@ -151,7 +173,6 @@
},
{
"cell_type": "markdown",
- "id": "acoustic-congo",
"metadata": {},
"source": [
"Simple Python objects are translated to JSON according to a fairly intuitive conversion."
@@ -159,7 +180,6 @@
},
{
"cell_type": "markdown",
- "id": "preliminary-turning",
"metadata": {},
"source": [
"|`Python`|`JSON`|\n",
@@ -175,17 +195,16 @@
},
{
"cell_type": "markdown",
- "id": "generic-credits",
"metadata": {},
"source": [
- "## A Simple Serialization Example\n",
+ "\n",
+ "## A Simple Serialization Example [
](#table_of_contents)\n",
"Imagine you’re working with a Python object in memory that looks a little something like this:"
]
},
{
"cell_type": "code",
- "execution_count": 26,
- "id": "south-obligation",
+ "execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
@@ -209,17 +228,16 @@
},
{
"cell_type": "code",
- "execution_count": 22,
- "id": "coral-philadelphia",
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "'{\"students\": [{\"Name\": \"Ali Hejazizo\", \"Grade\": [14.0, 20]}, {\"Name\": \"Masoud Zarepoor\", \"Grade\": null}]}'"
+ "'{\"students\": [{\"Name\": \"Ali Hejazizo\", \"Grade\": [14.0, 20]}, {\"Name\": \"Masoud Zarepoor\", \"Grade\": null, \"is_in_class\": true}]}'"
]
},
- "execution_count": 22,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -232,18 +250,17 @@
},
{
"cell_type": "code",
- "execution_count": 23,
- "id": "accessory-testing",
+ "execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'students': [{'Name': 'Ali Hejazizo', 'Grade': [14.0, 20]},\n",
- " {'Name': 'Masoud Zarepoor', 'Grade': None}]}"
+ " {'Name': 'Masoud Zarepoor', 'Grade': None, 'is_in_class': True}]}"
]
},
- "execution_count": 23,
+ "execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@@ -255,7 +272,6 @@
},
{
"cell_type": "markdown",
- "id": "documented-bible",
"metadata": {},
"source": [
"It is critical that you save this information to disk, so your mission is to write it to a file."
@@ -263,7 +279,6 @@
},
{
"cell_type": "markdown",
- "id": "black-pierce",
"metadata": {},
"source": [
"Using Python’s context manager, you can create a file called `data_file.json` and open it in write mode. (JSON files conveniently end in a `.json` extension.)"
@@ -272,7 +287,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "civil-peter",
"metadata": {},
"outputs": [],
"source": [
@@ -285,7 +299,6 @@
{
"cell_type": "code",
"execution_count": 35,
- "id": "grave-honor",
"metadata": {},
"outputs": [],
"source": [
@@ -296,7 +309,6 @@
{
"cell_type": "code",
"execution_count": 36,
- "id": "classified-arbor",
"metadata": {},
"outputs": [
{
@@ -317,7 +329,6 @@
},
{
"cell_type": "markdown",
- "id": "swedish-leisure",
"metadata": {},
"source": [
"Note that `dump()` takes two positional arguments:\n",
@@ -328,7 +339,6 @@
},
{
"cell_type": "markdown",
- "id": "urban-magazine",
"metadata": {},
"source": [
"Or, if you were so inclined as to continue using this serialized JSON data in your program, you could write it to a native Python `str` object."
@@ -337,7 +347,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "bigger-rainbow",
"metadata": {},
"outputs": [],
"source": [
@@ -347,7 +356,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "amber-brisbane",
"metadata": {},
"outputs": [
{
@@ -367,7 +375,6 @@
},
{
"cell_type": "markdown",
- "id": "stainless-inventory",
"metadata": {},
"source": [
"Notice that the file-like object is absent since you aren’t actually writing to disk. Other than that, `dumps()` is just like `dump()`."
@@ -375,15 +382,14 @@
},
{
"cell_type": "markdown",
- "id": "viral-preparation",
"metadata": {},
"source": [
- "## Some Useful Keyword Arguments"
+ "\n",
+ "## Some Useful Keyword Arguments [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "graphic-backing",
"metadata": {},
"source": [
"Remember, JSON is meant to be easily readable by humans, but readable syntax isn’t enough if it’s all squished together. Plus you’ve probably got a different programming style than me, and it might be easier for you to read code when it’s formatted to your liking."
@@ -391,7 +397,6 @@
},
{
"cell_type": "markdown",
- "id": "alone-layer",
"metadata": {},
"source": [
"> **NOTE:** Both the `dump()` and `dumps()` methods use the same keyword arguments."
@@ -399,7 +404,6 @@
},
{
"cell_type": "markdown",
- "id": "disabled-density",
"metadata": {},
"source": [
"The first option most people want to change is whitespace. You can use the `indent` keyword argument to specify the indentation size for nested structures. Check out the difference for yourself by using `data`, which we defined above, and running the following commands in a console:"
@@ -408,7 +412,6 @@
{
"cell_type": "code",
"execution_count": 37,
- "id": "forced-moment",
"metadata": {},
"outputs": [
{
@@ -426,7 +429,6 @@
{
"cell_type": "code",
"execution_count": 41,
- "id": "brilliant-blink",
"metadata": {},
"outputs": [
{
@@ -459,7 +461,6 @@
{
"cell_type": "code",
"execution_count": 43,
- "id": "fitted-hughes",
"metadata": {},
"outputs": [],
"source": [
@@ -469,7 +470,6 @@
},
{
"cell_type": "markdown",
- "id": "nuclear-camping",
"metadata": {},
"source": [
"Another formatting option is the separators keyword argument. By default, this is a 2-tuple of the separator strings `(\", \", \": \")`, but a common alternative for compact JSON is `(\",\", \":\")`. Take a look at the sample JSON again to see where these separators come into play."
@@ -477,7 +477,6 @@
},
{
"cell_type": "markdown",
- "id": "married-ground",
"metadata": {},
"source": [
"There are others, like `sort_keys`, but I have no idea what that one does. You can find a whole list in the docs if you’re curious."
@@ -485,8 +484,7 @@
},
{
"cell_type": "code",
- "execution_count": 47,
- "id": "hollywood-rating",
+ "execution_count": 41,
"metadata": {},
"outputs": [
{
@@ -518,16 +516,15 @@
},
{
"cell_type": "markdown",
- "id": "plastic-basics",
"metadata": {},
"source": [
- "## Deserializing JSON\n",
+ "\n",
+ "## Deserializing JSON [
](#table_of_contents)\n",
"Great, looks like you’ve captured yourself some wild JSON! Now it’s time to whip it into shape. In the json library, you’ll find `load()` and `loads()` for turning JSON encoded data into Python objects."
]
},
{
"cell_type": "markdown",
- "id": "surprised-hotel",
"metadata": {},
"source": [
"Just like serialization, there is a simple conversion table for deserialization, though you can probably guess what it looks like already."
@@ -535,7 +532,6 @@
},
{
"cell_type": "markdown",
- "id": "statutory-cleaning",
"metadata": {},
"source": [
"|`JSON`|`Python`|\n",
@@ -552,7 +548,6 @@
},
{
"cell_type": "markdown",
- "id": "abandoned-demographic",
"metadata": {},
"source": [
"Technically, this conversion isn’t a perfect inverse to the serialization table. That basically means that if you encode an object now and then decode it again later, you may not get exactly the same object back. I imagine it’s a bit like teleportation: break my molecules down over here and put them back together over there. Am I still the same person?"
@@ -560,7 +555,6 @@
},
{
"cell_type": "markdown",
- "id": "aerial-earthquake",
"metadata": {},
"source": [
"In reality, it’s probably more like getting one friend to translate something into Japanese and another friend to translate it back into English. Regardless, the simplest example would be encoding a `tuple` and getting back a `list` after decoding, like so:"
@@ -569,7 +563,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "lesser-israel",
"metadata": {},
"outputs": [],
"source": [
@@ -581,7 +574,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "still-luxembourg",
"metadata": {},
"outputs": [
{
@@ -602,7 +594,6 @@
{
"cell_type": "code",
"execution_count": 27,
- "id": "collective-flexibility",
"metadata": {},
"outputs": [
{
@@ -623,7 +614,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "respected-member",
"metadata": {},
"outputs": [
{
@@ -644,7 +634,6 @@
{
"cell_type": "code",
"execution_count": 29,
- "id": "authorized-prime",
"metadata": {},
"outputs": [
{
@@ -664,15 +653,14 @@
},
{
"cell_type": "markdown",
- "id": "removed-inclusion",
"metadata": {},
"source": [
- "### A Simple Deserialization Example"
+ "\n",
+ "### A Simple Deserialization Example [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "acoustic-generation",
"metadata": {},
"source": [
"This time, imagine you’ve got some data stored on disk that you’d like to manipulate in memory. You’ll still use the context manager, but this time you’ll open up the existing `data_file.json` in read mode"
@@ -681,7 +669,6 @@
{
"cell_type": "code",
"execution_count": 30,
- "id": "crucial-tissue",
"metadata": {},
"outputs": [],
"source": [
@@ -691,7 +678,6 @@
},
{
"cell_type": "markdown",
- "id": "sitting-group",
"metadata": {},
"source": [
"Things are pretty straightforward here, but keep in mind that the result of this method could return any of the allowed data types from the conversion table. This is only important if you’re loading in data you haven’t seen before. In most cases, the root object will be a `dict` or a `list`."
@@ -699,7 +685,6 @@
},
{
"cell_type": "markdown",
- "id": "married-punch",
"metadata": {},
"source": [
"If you’ve pulled JSON data in from another program or have otherwise obtained a string of JSON formatted data in Python, you can easily deserialize that with `loads()`, which naturally loads from a string:"
@@ -708,7 +693,6 @@
{
"cell_type": "code",
"execution_count": 31,
- "id": "dependent-holly",
"metadata": {},
"outputs": [],
"source": [
@@ -731,24 +715,30 @@
},
{
"cell_type": "markdown",
- "id": "smart-installation",
"metadata": {},
"source": [
- "## Encoding and Decoding Custom Python Objects\n",
+ "\n",
+ "## Encoding and Decoding Custom Python Objects [
](#table_of_contents)\n",
"\n",
"What happens when we try to serialize a custom object you’re working on?"
]
},
{
"cell_type": "code",
- "execution_count": 48,
- "id": "federal-graph",
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class CustomType:\n",
- " pass\n",
- "\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
"data = {\n",
" 'custom_type': CustomType()\n",
"}"
@@ -756,8 +746,7 @@
},
{
"cell_type": "code",
- "execution_count": 49,
- "id": "sonic-therapist",
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
@@ -767,11 +756,11 @@
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;32m~/anaconda3/envs/test/lib/python3.7/json/__init__.py\u001b[0m in \u001b[0;36mdumps\u001b[0;34m(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)\u001b[0m\n\u001b[1;32m 229\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mindent\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mseparators\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 230\u001b[0m default is None and not sort_keys and not kw):\n\u001b[0;32m--> 231\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_default_encoder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 232\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mJSONEncoder\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/anaconda3/envs/test/lib/python3.7/json/encoder.py\u001b[0m in \u001b[0;36mencode\u001b[0;34m(self, o)\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0;31m# exceptions aren't as detailed. The list call should be roughly\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[0;31m# equivalent to the PySequence_Fast that ''.join() would do.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 199\u001b[0;31m \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miterencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_one_shot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 200\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 201\u001b[0m \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/anaconda3/envs/test/lib/python3.7/json/encoder.py\u001b[0m in \u001b[0;36miterencode\u001b[0;34m(self, o, _one_shot)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkey_separator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitem_separator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort_keys\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m self.skipkeys, _one_shot)\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_iterencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,\n",
- "\u001b[0;32m~/anaconda3/envs/test/lib/python3.7/json/encoder.py\u001b[0m in \u001b[0;36mdefault\u001b[0;34m(self, o)\u001b[0m\n\u001b[1;32m 177\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 178\u001b[0m \"\"\"\n\u001b[0;32m--> 179\u001b[0;31m raise TypeError(f'Object of type {o.__class__.__name__} '\n\u001b[0m\u001b[1;32m 180\u001b[0m f'is not JSON serializable')\n\u001b[1;32m 181\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/var/folders/b4/tsp68dlx1gz9xlnpgbx21ytc0000gn/T/ipykernel_5941/100718814.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;32m~/opt/anaconda3/envs/py37/lib/python3.7/json/__init__.py\u001b[0m in \u001b[0;36mdumps\u001b[0;34m(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)\u001b[0m\n\u001b[1;32m 229\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mindent\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mseparators\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 230\u001b[0m default is None and not sort_keys and not kw):\n\u001b[0;32m--> 231\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_default_encoder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 232\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mJSONEncoder\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m~/opt/anaconda3/envs/py37/lib/python3.7/json/encoder.py\u001b[0m in \u001b[0;36mencode\u001b[0;34m(self, o)\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0;31m# exceptions aren't as detailed. The list call should be roughly\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[0;31m# equivalent to the PySequence_Fast that ''.join() would do.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 199\u001b[0;31m \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miterencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_one_shot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 200\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 201\u001b[0m \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m~/opt/anaconda3/envs/py37/lib/python3.7/json/encoder.py\u001b[0m in \u001b[0;36miterencode\u001b[0;34m(self, o, _one_shot)\u001b[0m\n\u001b[1;32m 255\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkey_separator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitem_separator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort_keys\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 256\u001b[0m self.skipkeys, _one_shot)\n\u001b[0;32m--> 257\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_iterencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 258\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 259\u001b[0m def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,\n",
+ "\u001b[0;32m~/opt/anaconda3/envs/py37/lib/python3.7/json/encoder.py\u001b[0m in \u001b[0;36mdefault\u001b[0;34m(self, o)\u001b[0m\n\u001b[1;32m 177\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 178\u001b[0m \"\"\"\n\u001b[0;32m--> 179\u001b[0;31m raise TypeError(f'Object of type {o.__class__.__name__} '\n\u001b[0m\u001b[1;32m 180\u001b[0m f'is not JSON serializable')\n\u001b[1;32m 181\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mTypeError\u001b[0m: Object of type CustomType is not JSON serializable"
]
}
@@ -782,7 +771,6 @@
},
{
"cell_type": "markdown",
- "id": "major-examination",
"metadata": {},
"source": [
"Although the `json` module can handle most built-in Python types, it doesn’t understand how to encode customized data types by default. It’s like trying to fit a square peg in a round hole—you need a buzzsaw and parental supervision."
@@ -790,15 +778,14 @@
},
{
"cell_type": "markdown",
- "id": "dense-nurse",
"metadata": {},
"source": [
- "### Simplifying Data Structures"
+ "\n",
+ "### Simplifying Data Structures [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "seventh-hollow",
"metadata": {},
"source": [
"Now, the question is how to deal with more complex data structures. Well, you could try to encode and decode the JSON by hand, but there’s a slightly more clever solution that’ll save you some work. Instead of going straight from the custom data type to JSON, you can throw in an intermediary step."
@@ -806,7 +793,6 @@
},
{
"cell_type": "markdown",
- "id": "leading-letters",
"metadata": {},
"source": [
"All you need to do is represent your data in terms of the built-in types `json` already understands. Essentially, you translate the more complex object into a simpler representation, which the json module then translates into JSON. It’s like the transitive property in mathematics: if `A = B` and `B = C`, then `A = C`."
@@ -814,7 +800,6 @@
},
{
"cell_type": "markdown",
- "id": "liberal-oxygen",
"metadata": {},
"source": [
"To get the hang of this, you’ll need a complex object to play with. You could use any custom class you like, but Python has a built-in type called `complex` for representing complex numbers, and it isn’t serializable by default. So, for the sake of these examples, your complex object is going to be a `complex` object. Confused yet?"
@@ -823,7 +808,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "choice-degree",
"metadata": {},
"outputs": [
{
@@ -845,7 +829,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "laughing-given",
"metadata": {},
"outputs": [
{
@@ -870,7 +853,6 @@
},
{
"cell_type": "markdown",
- "id": "experimental-fetish",
"metadata": {},
"source": [
"A good question to ask yourself when working with custom types is **What is the minimum amount of information necessary to recreate this object?** In the case of complex numbers, you only need to know the real and imaginary parts, both of which you can access as attributes on the `complex` object:"
@@ -879,7 +861,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "blessed-wells",
"metadata": {},
"outputs": [
{
@@ -899,7 +880,6 @@
},
{
"cell_type": "markdown",
- "id": "according-restriction",
"metadata": {},
"source": [
"Passing the same numbers into a `complex` constructor is enough to satisfy the `__eq__` comparison operator:"
@@ -908,7 +888,6 @@
{
"cell_type": "code",
"execution_count": 60,
- "id": "approximate-general",
"metadata": {},
"outputs": [
{
@@ -928,7 +907,6 @@
},
{
"cell_type": "markdown",
- "id": "continental-badge",
"metadata": {},
"source": [
"Breaking custom data types down into their essential components is critical to both the serialization and deserialization processes."
@@ -936,10 +914,10 @@
},
{
"cell_type": "markdown",
- "id": "funky-category",
"metadata": {},
"source": [
- "#### Encoding Custom Types\n",
+ "\n",
+ "#### Encoding Custom Types [
](#table_of_contents)\n",
"\n",
"To translate a custom object into JSON, all you need to do is provide an encoding function to the `dump()` method’s `default` parameter. The `json` module will call this function on any objects that aren’t natively serializable. Here’s a simple decoding function you can use for practice:"
]
@@ -947,7 +925,6 @@
{
"cell_type": "code",
"execution_count": 76,
- "id": "perceived-vacuum",
"metadata": {},
"outputs": [],
"source": [
@@ -961,7 +938,6 @@
},
{
"cell_type": "markdown",
- "id": "severe-brief",
"metadata": {},
"source": [
"Notice that you’re expected to raise a `TypeError` if you don’t get the kind of object you were expecting. This way, you avoid accidentally serializing any other object type. Now you can try encoding complex objects for yourself!"
@@ -970,7 +946,6 @@
{
"cell_type": "code",
"execution_count": 81,
- "id": "aggregate-spelling",
"metadata": {},
"outputs": [
{
@@ -991,7 +966,6 @@
{
"cell_type": "code",
"execution_count": 82,
- "id": "congressional-glass",
"metadata": {},
"outputs": [
{
@@ -1016,7 +990,6 @@
},
{
"cell_type": "markdown",
- "id": "casual-advance",
"metadata": {},
"source": [
"> **Why did we encode the complex number as a `tuple`?** Great question! That certainly wasn’t the only choice, nor is it necessarily the best choice. In fact, this wouldn’t be a very good representation if you ever wanted to decode the object later, as you’ll see shortly."
@@ -1024,7 +997,6 @@
},
{
"cell_type": "markdown",
- "id": "elder-neighbor",
"metadata": {},
"source": [
"The other common approach is to subclass the standard `JSONEncoder` and override its `default()` method:"
@@ -1033,7 +1005,6 @@
{
"cell_type": "code",
"execution_count": 83,
- "id": "based-activation",
"metadata": {},
"outputs": [],
"source": [
@@ -1047,7 +1018,6 @@
},
{
"cell_type": "markdown",
- "id": "nervous-grain",
"metadata": {},
"source": [
"Instead of raising the `TypeError` yourself, you can simply let the base class handle it. You can use this either directly in the `dump()` method via the cls parameter or by creating an instance of the encoder and calling its `encode()` method:"
@@ -1056,7 +1026,6 @@
{
"cell_type": "code",
"execution_count": 84,
- "id": "reverse-relations",
"metadata": {},
"outputs": [
{
@@ -1077,7 +1046,6 @@
{
"cell_type": "code",
"execution_count": 85,
- "id": "romance-conflict",
"metadata": {},
"outputs": [],
"source": [
@@ -1087,7 +1055,6 @@
{
"cell_type": "code",
"execution_count": 86,
- "id": "impaired-trading",
"metadata": {},
"outputs": [
{
@@ -1108,7 +1075,6 @@
{
"cell_type": "code",
"execution_count": 56,
- "id": "perceived-procedure",
"metadata": {},
"outputs": [
{
@@ -1134,15 +1100,14 @@
},
{
"cell_type": "markdown",
- "id": "adjacent-error",
"metadata": {},
"source": [
- "#### Decoding Custom Types"
+ "\n",
+ "#### Decoding Custom Types [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "assisted-louisiana",
"metadata": {},
"source": [
"While the real and imaginary parts of a complex number are absolutely necessary, they are actually not quite sufficient to recreate the object. This is what happens when you try encoding a complex number with the `ComplexEncoder` and then decoding the result:"
@@ -1151,7 +1116,6 @@
{
"cell_type": "code",
"execution_count": 89,
- "id": "waiting-aerospace",
"metadata": {},
"outputs": [],
"source": [
@@ -1161,7 +1125,6 @@
{
"cell_type": "code",
"execution_count": 90,
- "id": "reduced-mainstream",
"metadata": {},
"outputs": [
{
@@ -1182,7 +1145,6 @@
{
"cell_type": "code",
"execution_count": 91,
- "id": "mathematical-village",
"metadata": {},
"outputs": [
{
@@ -1202,7 +1164,6 @@
},
{
"cell_type": "markdown",
- "id": "signal-bones",
"metadata": {},
"source": [
"All you get back is a list, and you’d have to pass the values into a `complex` constructor if you wanted that complex object again. Recall our discussion about teleportation. What’s missing is metadata, or information about the type of data you’re encoding."
@@ -1210,7 +1171,6 @@
},
{
"cell_type": "markdown",
- "id": "developed-contribution",
"metadata": {},
"source": [
"I suppose the question you really ought ask yourself is What is the minimum amount of information that is both **necessary** and **sufficient** to recreate this object?"
@@ -1218,7 +1178,6 @@
},
{
"cell_type": "markdown",
- "id": "distinguished-transaction",
"metadata": {},
"source": [
"The `json` module expects all custom types to be expressed as `objects` in the JSON standard. For variety, you can create a JSON file this time called `complex_data.json` and add the following `object` representing a complex number:"
@@ -1227,7 +1186,6 @@
{
"cell_type": "code",
"execution_count": 96,
- "id": "unique-breeding",
"metadata": {},
"outputs": [
{
@@ -1249,7 +1207,6 @@
},
{
"cell_type": "markdown",
- "id": "vocal-strength",
"metadata": {},
"source": [
"See the clever bit? That `\"__complex__\"` key is the metadata we just talked about. It doesn’t really matter what the associated value is. To get this little hack to work, all you need to do is verify that the key exists:"
@@ -1258,7 +1215,6 @@
{
"cell_type": "code",
"execution_count": 107,
- "id": "inner-bicycle",
"metadata": {},
"outputs": [],
"source": [
@@ -1271,7 +1227,6 @@
},
{
"cell_type": "markdown",
- "id": "configured-lending",
"metadata": {},
"source": [
"If `\"__complex__\"` isn’t in the dictionary, you can just return the object and let the default decoder deal with it."
@@ -1279,7 +1234,6 @@
},
{
"cell_type": "markdown",
- "id": "federal-poster",
"metadata": {},
"source": [
"Every time the `load()` method attempts to parse an `object`, you are given the opportunity to intercede before the default decoder has its way with the data. You can do this by passing your decoding function to the `object_hook` parameter."
@@ -1287,7 +1241,6 @@
},
{
"cell_type": "markdown",
- "id": "bizarre-wireless",
"metadata": {},
"source": [
"Now play the same kind of game as before:"
@@ -1296,7 +1249,6 @@
{
"cell_type": "code",
"execution_count": 105,
- "id": "divine-battle",
"metadata": {},
"outputs": [],
"source": [
@@ -1308,7 +1260,6 @@
{
"cell_type": "code",
"execution_count": 106,
- "id": "grave-heath",
"metadata": {},
"outputs": [
{
@@ -1328,7 +1279,6 @@
},
{
"cell_type": "markdown",
- "id": "broke-liechtenstein",
"metadata": {},
"source": [
"While `object_hook` might feel like the counterpart to the `dump()` method’s `default` parameter, the analogy really begins and ends there."
@@ -1336,7 +1286,6 @@
},
{
"cell_type": "markdown",
- "id": "dressed-bruce",
"metadata": {},
"source": [
"This doesn’t just work with one object either. Try putting this list of complex numbers into `complex_data.json` and running the script again:"
@@ -1345,7 +1294,6 @@
{
"cell_type": "code",
"execution_count": 65,
- "id": "complimentary-wallace",
"metadata": {},
"outputs": [
{
@@ -1375,7 +1323,6 @@
},
{
"cell_type": "markdown",
- "id": "comprehensive-level",
"metadata": {},
"source": [
"If all goes well, you’ll get a list of complex objects:"
@@ -1384,7 +1331,6 @@
{
"cell_type": "code",
"execution_count": 66,
- "id": "surgical-rates",
"metadata": {},
"outputs": [],
"source": [
@@ -1396,7 +1342,6 @@
{
"cell_type": "code",
"execution_count": 67,
- "id": "impressive-measurement",
"metadata": {},
"outputs": [
{
@@ -1416,7 +1361,6 @@
},
{
"cell_type": "markdown",
- "id": "registered-subdivision",
"metadata": {},
"source": [
"You could also try subclassing `JSONDecoder` and overriding `object_hook`, but it’s better to stick with the lightweight solution whenever possible."
@@ -1425,7 +1369,6 @@
{
"cell_type": "code",
"execution_count": 108,
- "id": "pretty-catch",
"metadata": {},
"outputs": [],
"source": [
@@ -1442,7 +1385,6 @@
{
"cell_type": "code",
"execution_count": 109,
- "id": "dedicated-attribute",
"metadata": {},
"outputs": [],
"source": [
@@ -1454,7 +1396,6 @@
{
"cell_type": "code",
"execution_count": 110,
- "id": "korean-commercial",
"metadata": {},
"outputs": [
{
@@ -1474,15 +1415,14 @@
},
{
"cell_type": "markdown",
- "id": "quiet-cache",
"metadata": {},
"source": [
- "# All done!"
+ "\n",
+ "#
All done! \n"
]
},
{
"cell_type": "markdown",
- "id": "peaceful-permission",
"metadata": {},
"source": [
"Congratulations, you can now wield the mighty power of JSON for any and all of your nefarious Python needs.\n",
@@ -1499,7 +1439,6 @@
},
{
"cell_type": "markdown",
- "id": "expressed-burning",
"metadata": {},
"source": [
"you captured and tamed some wild JSON, and you made it back in time for supper! As an added bonus, learning the `json` package will make learning `pickle` and marshal a snap."
@@ -1522,7 +1461,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/05. Modules/14.2 pickle.ipynb b/Python/05. Modules/01.2 pickle.ipynb
similarity index 93%
rename from python/05. Modules/14.2 pickle.ipynb
rename to Python/05. Modules/01.2 pickle.ipynb
index 9672b89..d702e1f 100755
--- a/python/05. Modules/14.2 pickle.ipynb
+++ b/Python/05. Modules/01.2 pickle.ipynb
@@ -2,15 +2,39 @@
"cells": [
{
"cell_type": "markdown",
- "id": "ancient-capitol",
"metadata": {},
"source": [
- "# Persist Objects in Python"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
Persist Objects in Python \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents \n",
+ "\n",
+ "\n",
+ "* [Serialization in Python](#serialization_in_python)\n",
+ "* [Inside the Python pickle Module](#inside_the_python_pickle_module)\n",
+ "* [Protocol Formats of the Python pickle Module](#protocol_formats_of_the_python_pickle_module)\n",
+ "* [Picklable and Unpicklable Types](#picklable_and_unpicklable_types)\n",
+ "* [Compression of Pickled Objects](#compression_of_pickled_objects)\n",
+ "* [Security Concerns With the Python pickle Module](#security_concerns_with_the_python_pickle_module)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "artistic-mining",
"metadata": {},
"source": [
"As a developer, you may sometimes need to send complex object hierarchies over a network or save the internal state of your objects to a disk or database for later use. To accomplish this, you can use a process called serialization, which is fully supported by the standard library thanks to the Python `pickle` module."
@@ -18,7 +42,6 @@
},
{
"cell_type": "markdown",
- "id": "italian-alignment",
"metadata": {},
"source": [
"In this section, you’ll learn:\n",
@@ -34,15 +57,14 @@
},
{
"cell_type": "markdown",
- "id": "extra-plenty",
"metadata": {},
"source": [
- "## Serialization in Python"
+ "\n",
+ "## Serialization in Python [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "alternative-christianity",
"metadata": {},
"source": [
"The **serialization** process is a way to convert a data structure into a linear form that can be stored or transmitted over a network."
@@ -50,7 +72,6 @@
},
{
"cell_type": "markdown",
- "id": "educational-cooper",
"metadata": {},
"source": [
"In Python, serialization allows you to take a complex object structure and transform it into a stream of bytes that can be saved to a disk or sent over a network. You may also see this process referred to as **marshalling**. The reverse process, which takes a stream of bytes and converts it back into a data structure, is called **deserialization** or **unmarshalling**."
@@ -58,7 +79,6 @@
},
{
"cell_type": "markdown",
- "id": "metropolitan-breath",
"metadata": {},
"source": [
"Serialization can be used in a lot of different situations. One of the most common uses is saving the state of a neural network after the training phase so that you can use it later without having to redo the training."
@@ -66,7 +86,6 @@
},
{
"cell_type": "markdown",
- "id": "latin-european",
"metadata": {},
"source": [
"Python offers three different modules in the standard library that allow you to serialize and deserialize objects:\n",
@@ -78,7 +97,6 @@
},
{
"cell_type": "markdown",
- "id": "annoying-religion",
"metadata": {},
"source": [
"In addition, Python supports [`XML`](https://www.xml.com/axml/axml.html), which you can also use to serialize objects."
@@ -86,7 +104,6 @@
},
{
"cell_type": "markdown",
- "id": "moved-active",
"metadata": {},
"source": [
"The `marshal` module is the oldest of the three listed above. It exists mainly to read and write the compiled bytecode of Python modules, or the `.pyc` files you get when the interpreter imports a Python module. So, even though you can use `marshal` to serialize some of your objects, it’s not recommended."
@@ -94,7 +111,6 @@
},
{
"cell_type": "markdown",
- "id": "adjustable-prescription",
"metadata": {},
"source": [
"The `json` module is the newest of the three. It allows you to work with standard JSON files. JSON is a very convenient and widely used format for data exchange."
@@ -102,7 +118,6 @@
},
{
"cell_type": "markdown",
- "id": "moving-toolbox",
"metadata": {},
"source": [
"There are several reasons to choose the JSON format: It’s human readable and language independent, and it’s lighter than XML. With the `json` module, you can serialize and deserialize several standard Python types:\n",
@@ -119,7 +134,6 @@
},
{
"cell_type": "markdown",
- "id": "dietary-talent",
"metadata": {},
"source": [
"The Python `pickle` module is another way to serialize and deserialize objects in Python. It differs from the `json` module in that it serializes objects in a binary format, which means the result is not human readable. However, it’s also faster and it works with many more Python types right out of the box, including your custom-defined objects."
@@ -127,7 +141,6 @@
},
{
"cell_type": "markdown",
- "id": "greek-poverty",
"metadata": {},
"source": [
"**Note:** From now on, you’ll see the terms **pickling** and **unpickling** used to refer to serializing and deserializing with the Python `pickle` module."
@@ -135,7 +148,6 @@
},
{
"cell_type": "markdown",
- "id": "respected-klein",
"metadata": {},
"source": [
"So, you have several different ways to serialize and deserialize objects in Python. But which one should you use? The short answer is that there’s no one-size-fits-all solution. It all depends on your use case."
@@ -143,7 +155,6 @@
},
{
"cell_type": "markdown",
- "id": "retired-adoption",
"metadata": {},
"source": [
"Here are three general guidelines for deciding which approach to use:\n",
@@ -157,15 +168,14 @@
},
{
"cell_type": "markdown",
- "id": "protective-champion",
"metadata": {},
"source": [
- "## Inside the Python pickle Module"
+ "\n",
+ "## Inside the Python pickle Module [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "cooked-noise",
"metadata": {},
"source": [
"The Python pickle module basically consists of four methods:\n",
@@ -179,7 +189,6 @@
},
{
"cell_type": "markdown",
- "id": "spoken-forth",
"metadata": {},
"source": [
"The first two methods are used during the pickling process, and the other two are used during unpickling. The only difference between `dump()` and `dumps()` is that the first creates a file containing the serialization result, whereas the second returns a string."
@@ -187,7 +196,6 @@
},
{
"cell_type": "markdown",
- "id": "english-apparel",
"metadata": {},
"source": [
"To differentiate `dumps()` from `dump()`, it’s helpful to remember that **the `s` at the end of the function name stands for `string`**. The same concept also applies to `load()` and `loads()`: The first one reads a file to start the unpickling process, and the second one operates on a string."
@@ -195,7 +203,6 @@
},
{
"cell_type": "markdown",
- "id": "green-butler",
"metadata": {},
"source": [
"Consider the following example. Say you have a custom-defined class named `example_class` with several different attributes, each of a different type:\n",
@@ -209,7 +216,6 @@
},
{
"cell_type": "markdown",
- "id": "narrative-ethnic",
"metadata": {},
"source": [
"The example below shows how you can instantiate the class and pickle the instance to get a plain string. After pickling the class, you can change the value of its attributes without affecting the pickled string. You can then unpickle the pickled string in another variable, restoring an exact copy of the previously pickled class:"
@@ -218,7 +224,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "blank-desktop",
"metadata": {},
"outputs": [
{
@@ -258,7 +263,6 @@
},
{
"cell_type": "markdown",
- "id": "general-configuration",
"metadata": {},
"source": [
"In the example above, you create several different objects and serialize them with `pickle`. This produces a single string with the serialized result."
@@ -266,7 +270,6 @@
},
{
"cell_type": "markdown",
- "id": "distributed-bermuda",
"metadata": {},
"source": [
"The pickling process ends correctly, storing your entire instance in this string: `b'\\x80\\x03c__main__\\nexample_class\\nq\\x00)\\x81q\\x01.'`. After the pickling process ends, you modify your original object by setting the attribute `a_dict` to `None.\n",
@@ -276,15 +279,14 @@
},
{
"cell_type": "markdown",
- "id": "assured-floor",
"metadata": {},
"source": [
- "## Protocol Formats of the Python pickle Module"
+ "\n",
+ "## Protocol Formats of the Python pickle Module [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "thermal-eligibility",
"metadata": {},
"source": [
"As mentioned above, the `pickle` module is Python-specific, and the result of a pickling process can be read only by another Python program. But even if you’re working with Python, it’s important to know that the `pickle` module has evolved over time."
@@ -292,7 +294,6 @@
},
{
"cell_type": "markdown",
- "id": "divided-circulation",
"metadata": {},
"source": [
"This means that if you’ve pickled an object with a specific version of Python, then you may not be able to unpickle it with an older version. The compatibility depends on the protocol version that you used for the pickling process."
@@ -300,7 +301,6 @@
},
{
"cell_type": "markdown",
- "id": "entitled-cambridge",
"metadata": {},
"source": [
"There are currently six different protocols that the Python pickle module can use. The higher the protocol version, the more recent the Python interpreter needs to be for unpickling."
@@ -308,7 +308,6 @@
},
{
"cell_type": "markdown",
- "id": "multiple-latest",
"metadata": {},
"source": [
"- **Protocol version 0** was the first version. Unlike later protocols, it’s human readable.\n",
@@ -321,7 +320,6 @@
},
{
"cell_type": "markdown",
- "id": "hawaiian-worship",
"metadata": {},
"source": [
"Note: Newer versions of the protocol offer more features and improvements but are limited to higher versions of the interpreter. Be sure to consider this when choosing which protocol to use.\n",
@@ -332,7 +330,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "according-illness",
"metadata": {},
"outputs": [
{
@@ -352,7 +349,6 @@
},
{
"cell_type": "markdown",
- "id": "rotary-three",
"metadata": {},
"source": [
"To choose a specific protocol, you need to specify the protocol version when you invoke `load()`, `loads()`, `dump()` or `dumps()`. If you don’t specify a protocol, then your interpreter will use the default version specified in the `pickle.DEFAULT_PROTOCOL` attribute."
@@ -360,15 +356,14 @@
},
{
"cell_type": "markdown",
- "id": "czech-credits",
"metadata": {},
"source": [
- "## Picklable and Unpicklable Types"
+ "\n",
+ "## Picklable and Unpicklable Types [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "danish-causing",
"metadata": {},
"source": [
"You’ve already learned that the Python `pickle` module can serialize many more types than the json module. However, not everything is picklable. The list of unpicklable objects includes database connections, opened network sockets, running threads, and others.\n",
@@ -378,7 +373,6 @@
},
{
"cell_type": "markdown",
- "id": "gorgeous-recorder",
"metadata": {},
"source": [
"The `dill` module extends the capabilities of `pickle`. According to the official documentation, it lets you serialize less common types like functions with yields, nested functions, lambdas, and many others."
@@ -386,7 +380,6 @@
},
{
"cell_type": "markdown",
- "id": "broad-vietnamese",
"metadata": {},
"source": [
"The `dill` module extends the capabilities of `pickle`. According to the official documentation, it lets you serialize less common types like functions with yields, nested functions, lambdas, and many others."
@@ -394,7 +387,6 @@
},
{
"cell_type": "markdown",
- "id": "constant-jersey",
"metadata": {},
"source": [
"To test this module, you can try to pickle a `lambda` function.\n",
@@ -405,7 +397,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "cubic-crack",
"metadata": {},
"outputs": [
{
@@ -430,7 +421,6 @@
},
{
"cell_type": "markdown",
- "id": "unsigned-recipe",
"metadata": {},
"source": [
"Now try replacing the Python `pickle` module with `dill` to see if there’s any difference.\n",
@@ -441,7 +431,6 @@
{
"cell_type": "code",
"execution_count": 17,
- "id": "increasing-motor",
"metadata": {},
"outputs": [
{
@@ -463,7 +452,6 @@
},
{
"cell_type": "markdown",
- "id": "capital-tolerance",
"metadata": {},
"source": [
"**Note:** Before you use `dill` instead of pickle, keep in mind that `dill` is not included in the standard library of the Python interpreter and is typically slower than `pickle`."
@@ -471,7 +459,6 @@
},
{
"cell_type": "markdown",
- "id": "selected-zealand",
"metadata": {},
"source": [
"Another interesting feature of `dill` is that it can even serialize an entire interpreter session. Here’s an example:"
@@ -480,7 +467,6 @@
{
"cell_type": "code",
"execution_count": 18,
- "id": "sublime-kruger",
"metadata": {},
"outputs": [],
"source": [
@@ -494,7 +480,6 @@
},
{
"cell_type": "markdown",
- "id": "first-geology",
"metadata": {},
"source": [
"In this example, you start the interpreter, import a module, and define a `lambda` function along with a couple of other variables. You then import the `dill` module and `invoke dump_session()` to serialize the entire session."
@@ -502,7 +487,6 @@
},
{
"cell_type": "markdown",
- "id": "religious-collector",
"metadata": {},
"source": [
"If everything goes okay, then you should get a `test.pkl` file in your current directory:"
@@ -510,7 +494,6 @@
},
{
"cell_type": "markdown",
- "id": "military-modeling",
"metadata": {},
"source": [
"Now you can start a new instance of the interpreter and load the `test.pkl` file to restore your last session:"
@@ -519,7 +502,6 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "extreme-medicine",
"metadata": {},
"outputs": [
{
@@ -540,7 +522,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "opponent-dealer",
"metadata": {},
"outputs": [],
"source": [
@@ -551,7 +532,6 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "initial-penalty",
"metadata": {},
"outputs": [
{
@@ -572,7 +552,6 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "personal-university",
"metadata": {},
"outputs": [
{
@@ -593,7 +572,6 @@
{
"cell_type": "code",
"execution_count": 7,
- "id": "nuclear-disaster",
"metadata": {},
"outputs": [
{
@@ -614,7 +592,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "wanted-baghdad",
"metadata": {},
"outputs": [
{
@@ -634,7 +611,6 @@
},
{
"cell_type": "markdown",
- "id": "ceramic-constitutional",
"metadata": {},
"source": [
"The first `globals().items()` statement demonstrates that the interpreter is in the initial state. This means that you need to import the `dill` module and call `load_session()` to restore your serialized interpreter session."
@@ -642,7 +618,6 @@
},
{
"cell_type": "markdown",
- "id": "scheduled-colonial",
"metadata": {},
"source": [
"Even though `dill` lets you serialize a wider range of objects than `pickle`, it can’t solve every serialization problem that you may have. If you need to serialize an object that contains a database connection, for example, then you’re in for a tough time because it’s an unserializable object even for `dill`."
@@ -650,7 +625,6 @@
},
{
"cell_type": "markdown",
- "id": "frequent-enterprise",
"metadata": {},
"source": [
"So, how can you solve this problem?"
@@ -658,7 +632,6 @@
},
{
"cell_type": "markdown",
- "id": "confidential-rouge",
"metadata": {},
"source": [
"The solution in this case is to exclude the object from the serialization process and to **reinitialize** the connection after the object is deserialized."
@@ -666,7 +639,6 @@
},
{
"cell_type": "markdown",
- "id": "tamil-possible",
"metadata": {},
"source": [
"In the following example, you’ll see how you can define a class with several attributes and exclude one attribute from serialization with `__getstate()__`:"
@@ -675,7 +647,6 @@
{
"cell_type": "code",
"execution_count": 24,
- "id": "urban-springer",
"metadata": {},
"outputs": [],
"source": [
@@ -697,7 +668,6 @@
{
"cell_type": "code",
"execution_count": 25,
- "id": "structural-cholesterol",
"metadata": {},
"outputs": [
{
@@ -718,7 +688,6 @@
},
{
"cell_type": "markdown",
- "id": "interracial-energy",
"metadata": {},
"source": [
"In this example, you create an object with three attributes. Since one attribute is a `lambda`, the object is unpicklable with the standard `pickle` module."
@@ -726,7 +695,6 @@
},
{
"cell_type": "markdown",
- "id": "minute-nigeria",
"metadata": {},
"source": [
"To address this issue, you specify what to pickle with `__getstate__()`. You first clone the entire `__dict__` of the instance to have all the attributes defined in the class, and then you manually remove the unpicklable `c` attribute."
@@ -734,7 +702,6 @@
},
{
"cell_type": "markdown",
- "id": "downtown-spyware",
"metadata": {},
"source": [
"If you run this example and then deserialize the object, then you’ll see that the new instance doesn’t contain the `c` attribute."
@@ -742,7 +709,6 @@
},
{
"cell_type": "markdown",
- "id": "hundred-suggestion",
"metadata": {},
"source": [
"But what if you wanted to do some additional initializations while unpickling, say by adding the excluded `c` object back to the deserialized instance? You can accomplish this with `__setstate__()`."
@@ -751,7 +717,6 @@
{
"cell_type": "code",
"execution_count": 26,
- "id": "furnished-weekend",
"metadata": {},
"outputs": [
{
@@ -789,7 +754,6 @@
},
{
"cell_type": "markdown",
- "id": "equipped-drive",
"metadata": {},
"source": [
"By passing the excluded `c` object to `__setstate__()`, you ensure that it appears in the `__dict__` of the unpickled string."
@@ -797,15 +761,14 @@
},
{
"cell_type": "markdown",
- "id": "fuzzy-brunswick",
"metadata": {},
"source": [
- "## Compression of Pickled Objects"
+ "\n",
+ "## Compression of Pickled Objects [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "floppy-interval",
"metadata": {},
"source": [
"Although the `pickle` data format is a compact binary representation of an object structure, you can still optimize your pickled string by compressing it with `bzip2` or `gzip`."
@@ -813,7 +776,6 @@
},
{
"cell_type": "markdown",
- "id": "secondary-archives",
"metadata": {},
"source": [
"To compress a pickled string with `bzip2`, you can use the `bz2` module provided in the standard library."
@@ -821,7 +783,6 @@
},
{
"cell_type": "markdown",
- "id": "regular-english",
"metadata": {},
"source": [
"In the following example, you’ll take a string, pickle it, and then compress it using the `bz2` library:"
@@ -830,7 +791,6 @@
{
"cell_type": "code",
"execution_count": 11,
- "id": "romantic-property",
"metadata": {},
"outputs": [
{
@@ -866,7 +826,6 @@
},
{
"cell_type": "markdown",
- "id": "scientific-contractor",
"metadata": {},
"source": [
"When using compression, bear in mind that smaller files come at the cost of a slower process."
@@ -874,15 +833,14 @@
},
{
"cell_type": "markdown",
- "id": "broadband-torture",
"metadata": {},
"source": [
- "## Security Concerns With the Python pickle Module"
+ "\n",
+ "## Security Concerns With the Python pickle Module [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "endless-spank",
"metadata": {},
"source": [
"You now know how to use the `pickle` module to serialize and deserialize objects in Python. The serialization process is very convenient when you need to save your object’s state to disk or to transmit it over a network."
@@ -890,7 +848,6 @@
},
{
"cell_type": "markdown",
- "id": "demanding-banner",
"metadata": {},
"source": [
"However, there’s one more thing you need to know about the Python `pickle` module: It’s not secure. Do you remember the discussion of `__setstate__()`? Well, that method is great for doing more initialization while unpickling, but it can also be used to execute arbitrary code during the unpickling process!"
@@ -898,7 +855,6 @@
},
{
"cell_type": "markdown",
- "id": "cutting-prediction",
"metadata": {},
"source": [
"So, what can you do to reduce this risk?"
@@ -906,7 +862,6 @@
},
{
"cell_type": "markdown",
- "id": "cultural-fusion",
"metadata": {},
"source": [
"Sadly, not much. The rule of thumb is to **never unpickle data that comes from an untrusted source or is transmitted over an insecure network**. In order to prevent man-in-the-middle attacks, it’s a good idea to use libraries such as `hmac` to sign the data and ensure it hasn’t been tampered with.\n",
@@ -917,7 +872,6 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "secret-magazine",
"metadata": {},
"outputs": [],
"source": [
@@ -941,7 +895,6 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "crucial-vocabulary",
"metadata": {},
"outputs": [],
"source": [
@@ -951,7 +904,6 @@
{
"cell_type": "code",
"execution_count": null,
- "id": "engaging-porcelain",
"metadata": {},
"outputs": [],
"source": [
@@ -962,7 +914,6 @@
},
{
"cell_type": "markdown",
- "id": "facial-model",
"metadata": {},
"source": [
"In this example, the unpickling process executes `__setstate__()`, which executes a Bash command to open a remote shell to the `192.168.1.10` machine on port `8080`."
@@ -970,7 +921,6 @@
},
{
"cell_type": "markdown",
- "id": "alike-photographer",
"metadata": {},
"source": [
"Here’s how you can safely test this script on your Mac or your Linux box. First, open the terminal and use the nc command to listen for a connection to port 8080:\n",
@@ -982,7 +932,6 @@
},
{
"cell_type": "markdown",
- "id": "horizontal-guard",
"metadata": {},
"source": [
"This will be the attacker terminal. If everything works, then the command will seem to hang."
@@ -990,7 +939,6 @@
},
{
"cell_type": "markdown",
- "id": "massive-bleeding",
"metadata": {},
"source": [
"Next, open another terminal on the same computer (or on any other computer on the network) and execute the Python code above for unpickling the malicious code. Be sure to change the IP address in the code to your attacking terminal’s IP address. In my example, the attacker’s IP address is `192.168.1.10`."
@@ -998,7 +946,6 @@
},
{
"cell_type": "markdown",
- "id": "czech-survey",
"metadata": {},
"source": [
"By executing this code, the victim will expose a shell to the attacker."
@@ -1006,7 +953,6 @@
},
{
"cell_type": "markdown",
- "id": "broadband-james",
"metadata": {},
"source": [
"If everything works, a Bash shell will appear on the attacking console. This console can now operate directly on the attacked system:\n",
@@ -1024,7 +970,6 @@
},
{
"cell_type": "markdown",
- "id": "interpreted-breathing",
"metadata": {},
"source": [
"So, let me repeat this critical point once again: **Do not use the `pickle` module to deserialize objects from untrusted sources!**"
@@ -1032,17 +977,16 @@
},
{
"cell_type": "markdown",
- "id": "narrow-separation",
"metadata": {},
"source": [
- "## Conclusion\n",
+ "\n",
+ "##
Conclusion [
](#table_of_contents)\n",
"\n",
"You now know how to use the Python `pickle` module to convert an object hierarchy to a stream of bytes that can be saved to a disk or transmitted over a network. You also know that the deserialization process in Python must be used with care since unpickling something that comes from an untrusted source can be extremely dangerous."
]
},
{
"cell_type": "markdown",
- "id": "protecting-danger",
"metadata": {},
"source": [
"In this tutorial, you’ve learned:\n",
@@ -1073,7 +1017,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/05. Modules/14.3 yaml.ipynb b/Python/05. Modules/01.3 yaml.ipynb
similarity index 75%
rename from python/05. Modules/14.3 yaml.ipynb
rename to Python/05. Modules/01.3 yaml.ipynb
index 6080bca..473ac54 100755
--- a/python/05. Modules/14.3 yaml.ipynb
+++ b/Python/05. Modules/01.3 yaml.ipynb
@@ -2,15 +2,54 @@
"cells": [
{
"cell_type": "markdown",
- "id": "floating-values",
"metadata": {},
"source": [
- "# YAML"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
YAML \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "\n",
+ "* [YAML Tutorial Quick Start: A Simple File](#yaml_tutorial_quick_start:_a_simple_file)\n",
+ "* [Installation](#installation)\n",
+ "* [Read YAML](#read_yaml)\n",
+ "* [Introduction](#introduction)\n",
+ " * [Rules for Creating YAML file](#rules_for_creating_yaml_file)\n",
+ " * [Indentation of YAML](#indentation_of_yaml)\n",
+ " * [Basics Components](#basics_components)\n",
+ " * [Conventional Block Format](#conventional_block_format)\n",
+ " * [Inline Format](#inline_format)\n",
+ " * [Folded Text](#folded_text)\n",
+ "* [YAML Syntax and format](#yaml_syntax_and_format)\n",
+ " * [String](#string)\n",
+ " * [Short texts](#short_texts)\n",
+ " * [Long text](#long_text)\n",
+ " * [Numbers](#numbers)\n",
+ " * [Boolean](#boolean)\n",
+ " * [Collections and Structures](#collections_and_structures)\n",
+ " * [Lists](#lists)\n",
+ " * [Maps](#maps)\n",
+ " * [References](#references)\n",
+ " * [Multiple documents](#multiple_documents)\n",
+ " * [Tags](#tags)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "contained-running",
"metadata": {},
"source": [
"**Y**AML **A**in't **M**arkup **L**anguage (**YAML**) is a serialization language that has steadily increased in popularity over the last few years. It's often used as a format for configuration files, but its object serialization abilities make it a viable replacement for languages like JSON. This YAML tutorial will demonstrate the language syntax with a guide and some simple coding examples in Python. YAML has broad language support and maps easily into native data structures. It's also easy to for humans to read, which is why it's a good choice for configuration. The YAML acronym was shorthand for **Yet Another Markup Language**. But the maintainers renamed it to **YAML Ain't Markup Language** to place more emphasis on its data-oriented features."
@@ -18,7 +57,6 @@
},
{
"cell_type": "markdown",
- "id": "reasonable-proportion",
"metadata": {},
"source": [
"YAML is a recently introduced data serialization format and is very comfortable for human reading and writing. **YAML is poised to replace XML and JSON**."
@@ -26,15 +64,14 @@
},
{
"cell_type": "markdown",
- "id": "apparent-makeup",
"metadata": {},
"source": [
- "## YAML Tutorial Quick Start: A Simple File"
+ "\n",
+ "## YAML Tutorial Quick Start: A Simple File [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "intense-visitor",
"metadata": {},
"source": [
"Let's take a look at a YAML file for a brief overview."
@@ -42,7 +79,6 @@
},
{
"cell_type": "markdown",
- "id": "tropical-chicken",
"metadata": {},
"source": [
"```yaml\n",
@@ -80,7 +116,6 @@
},
{
"cell_type": "markdown",
- "id": "cooperative-johns",
"metadata": {},
"source": [
"The identical `json` file is:"
@@ -88,7 +123,6 @@
},
{
"cell_type": "markdown",
- "id": "expanded-amino",
"metadata": {},
"source": [
"```json\n",
@@ -138,7 +172,6 @@
},
{
"cell_type": "markdown",
- "id": "alpine-center",
"metadata": {},
"source": [
"JSON and YAML have similar capabilities, and you can convert most documents between the formats."
@@ -146,15 +179,14 @@
},
{
"cell_type": "markdown",
- "id": "equipped-police",
"metadata": {},
"source": [
- "## Installation"
+ "\n",
+ "## Installation [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "assisted-estate",
"metadata": {},
"source": [
"Install a Python module called the `pyyaml` to work with YAML files.\n",
@@ -166,15 +198,14 @@
},
{
"cell_type": "markdown",
- "id": "fossil-strategy",
"metadata": {},
"source": [
- "## Read YAML"
+ "\n",
+ "## Read YAML [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "seventh-representation",
"metadata": {},
"source": [
"Let's create a simple yaml file first:"
@@ -182,15 +213,14 @@
},
{
"cell_type": "code",
- "execution_count": 1,
- "id": "altered-gabriel",
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Overwriting learn_yaml.yaml\n"
+ "Writing learn_yaml.yaml\n"
]
}
],
@@ -201,7 +231,6 @@
},
{
"cell_type": "markdown",
- "id": "worthy-expert",
"metadata": {},
"source": [
"You can read a yaml file with `yaml.load`:"
@@ -210,7 +239,6 @@
{
"cell_type": "code",
"execution_count": 2,
- "id": "drawn-effectiveness",
"metadata": {},
"outputs": [
{
@@ -235,7 +263,6 @@
},
{
"cell_type": "markdown",
- "id": "greatest-windows",
"metadata": {},
"source": [
"Also, `yaml.load` can read from a string:"
@@ -244,7 +271,6 @@
{
"cell_type": "code",
"execution_count": 16,
- "id": "hawaiian-pacific",
"metadata": {},
"outputs": [
{
@@ -264,7 +290,6 @@
},
{
"cell_type": "markdown",
- "id": "conceptual-processing",
"metadata": {},
"source": [
"Consider the following point number of “pi”, which has a value of 3.1415926. In YAML, it is represented as a floating number as shown below:"
@@ -273,7 +298,6 @@
{
"cell_type": "code",
"execution_count": 19,
- "id": "confident-istanbul",
"metadata": {},
"outputs": [
{
@@ -293,7 +317,6 @@
},
{
"cell_type": "markdown",
- "id": "going-suicide",
"metadata": {},
"source": [
"Suppose, multiple values are to be loaded in specific data structure as mentioned below:\n",
@@ -309,7 +332,6 @@
{
"cell_type": "code",
"execution_count": 15,
- "id": "purple-calculator",
"metadata": {},
"outputs": [
{
@@ -337,7 +359,6 @@
},
{
"cell_type": "markdown",
- "id": "coral-interim",
"metadata": {},
"source": [
"**Note:** You can read on why calling `yaml.load` without `Loader` is deprecated in [here](https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation)"
@@ -345,7 +366,6 @@
},
{
"cell_type": "markdown",
- "id": "breeding-still",
"metadata": {},
"source": [
"To make it easier through this section, we define and use this function to parse yaml strings:"
@@ -353,8 +373,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
- "id": "coated-peoples",
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -364,15 +383,14 @@
},
{
"cell_type": "markdown",
- "id": "lovely-momentum",
"metadata": {},
"source": [
- "## Introduction"
+ "\n",
+ "## Introduction [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "practical-perry",
"metadata": {},
"source": [
"Now that you have an idea about YAML and its features, let us learn its basics with syntax and other operations. Remember that YAML includes a human readable structured format."
@@ -380,10 +398,10 @@
},
{
"cell_type": "markdown",
- "id": "enclosed-button",
"metadata": {},
"source": [
- "### Rules for Creating YAML file\n",
+ "\n",
+ "### Rules for Creating YAML file [
](#table_of_contents)\n",
"\n",
"When you are creating a file in YAML, you should remember the following basic rules:\n",
"\n",
@@ -394,10 +412,10 @@
},
{
"cell_type": "markdown",
- "id": "leading-gamma",
"metadata": {},
"source": [
- "### Indentation of YAML\n",
+ "\n",
+ "### Indentation of YAML [
](#table_of_contents)\n",
"\n",
"YAML does not include any mandatory spaces. Further, there is no need to be consistent. The valid YAML indentation is shown below."
]
@@ -405,7 +423,6 @@
{
"cell_type": "code",
"execution_count": 9,
- "id": "small-screening",
"metadata": {},
"outputs": [
{
@@ -435,7 +452,6 @@
},
{
"cell_type": "markdown",
- "id": "governmental-sleeve",
"metadata": {},
"source": [
"You should remember the following rules while working with indentation in YAML:\n",
@@ -447,15 +463,14 @@
},
{
"cell_type": "markdown",
- "id": "thirty-empty",
"metadata": {},
"source": [
- "### Basics Components"
+ "\n",
+ "### Basics Components [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "average-potential",
"metadata": {},
"source": [
"The basic components of YAML are described below."
@@ -463,17 +478,16 @@
},
{
"cell_type": "markdown",
- "id": "drawn-geometry",
"metadata": {},
"source": [
- "#### Conventional Block Format\n",
+ "\n",
+ "#### Conventional Block Format [
](#table_of_contents)\n",
"This block format uses hyphen+space to begin a new item in a specified list. Observe the example shown below:"
]
},
{
"cell_type": "code",
"execution_count": 25,
- "id": "attached-journey",
"metadata": {},
"outputs": [
{
@@ -499,7 +513,6 @@
},
{
"cell_type": "markdown",
- "id": "crazy-crystal",
"metadata": {},
"source": [
"**Note:** Comments in YAML begins with the `#` character. YAML does not support multi line comments."
@@ -507,10 +520,10 @@
},
{
"cell_type": "markdown",
- "id": "economic-music",
"metadata": {},
"source": [
- "#### Inline Format\n",
+ "\n",
+ "#### Inline Format [
](#table_of_contents)\n",
"\n",
"Inline format is delimited with comma and space and the items are enclosed in `JSON`. Observe the example shown below:"
]
@@ -518,7 +531,6 @@
{
"cell_type": "code",
"execution_count": 60,
- "id": "provincial-center",
"metadata": {},
"outputs": [
{
@@ -542,7 +554,6 @@
{
"cell_type": "code",
"execution_count": 57,
- "id": "helpful-ordinary",
"metadata": {},
"outputs": [
{
@@ -562,10 +573,10 @@
},
{
"cell_type": "markdown",
- "id": "average-miami",
"metadata": {},
"source": [
- "#### Folded Text\n",
+ "\n",
+ "#### Folded Text [
](#table_of_contents)\n",
"\n",
"Folded text converts newlines to spaces and removes the leading whitespace. Observe the example shown below:"
]
@@ -573,7 +584,6 @@
{
"cell_type": "code",
"execution_count": 59,
- "id": "explicit-military",
"metadata": {},
"outputs": [
{
@@ -597,15 +607,14 @@
},
{
"cell_type": "markdown",
- "id": "military-shift",
"metadata": {},
"source": [
- "## YAML Syntax and format"
+ "\n",
+ "## YAML Syntax and format [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "constant-spanish",
"metadata": {},
"source": [
"To begin with a YAML document, one needs to use `---`. All nested elements need to indented using two spaces but you can use one too. These three dashes are required only when there is more than one document within a single YAML file."
@@ -613,24 +622,23 @@
},
{
"cell_type": "markdown",
- "id": "nutritional-timer",
"metadata": {},
"source": [
- "### String"
+ "\n",
+ "### String [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "atmospheric-sheffield",
"metadata": {},
"source": [
- "#### Short texts\n",
+ "\n",
+ "#### Short texts [
](#table_of_contents)\n",
"Short texts are written, as shown below."
]
},
{
"cell_type": "markdown",
- "id": "unlike-mason",
"metadata": {},
"source": [
"```yaml\n",
@@ -642,17 +650,16 @@
},
{
"cell_type": "markdown",
- "id": "manual-reading",
"metadata": {},
"source": [
- "#### Long text\n",
+ "\n",
+ "#### Long text [
](#table_of_contents)\n",
"\n",
"Long texts are written using `|` and `>`. A block written using `|` is called a literal block, and a block written with `>` is called a folded block."
]
},
{
"cell_type": "markdown",
- "id": "talented-contact",
"metadata": {},
"source": [
"```yaml\n",
@@ -667,18 +674,46 @@
},
{
"cell_type": "markdown",
- "id": "instrumental-bridges",
"metadata": {},
"source": [
"The block style indicates how newlines inside the block should behave. If you would like them to be kept as **newlines**, use the **literal style**, indicated by a **pipe** (`|`). If instead you want them to be **replaced by spaces**, use the **folded style**, indicated by a **right angle bracket** (`>`). (To get a newline using the folded style, leave a blank line by putting two newlines in. Lines with extra indentation are also not folded.)"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'long description': 'This text preserves the line breaks,\\nas well as the indentation.\\n',\n",
+ " 'folded long description': 'This is also one way of writing the long text, but line breaks and indentations will be replaced with single spaces when read.\\n'}"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "read_yaml(\"\"\"\n",
+ "long description: |\n",
+ " This text preserves the line breaks,\n",
+ " as well as the indentation.\n",
+ "folded long description: >\n",
+ " This is also one way of writing the long text, but \n",
+ " line breaks and indentations will be replaced with single spaces when read.\n",
+ "\"\"\"\n",
+ ")"
+ ]
+ },
{
"cell_type": "markdown",
- "id": "waiting-evaluation",
"metadata": {},
"source": [
- "### Numbers\n",
+ "\n",
+ "### Numbers [
](#table_of_contents)\n",
"\n",
"Integer numbers are written as shown below:\n",
"\n",
@@ -692,7 +727,6 @@
},
{
"cell_type": "markdown",
- "id": "informed-wrapping",
"metadata": {},
"source": [
"Float numbers are written as shown below:\n",
@@ -708,10 +742,10 @@
},
{
"cell_type": "markdown",
- "id": "resistant-greensboro",
"metadata": {},
"source": [
- "### Boolean\n",
+ "\n",
+ "### Boolean [
](#table_of_contents)\n",
"\n",
"Boolean is written as true and false:\n",
"\n",
@@ -723,18 +757,18 @@
},
{
"cell_type": "markdown",
- "id": "radio-palace",
"metadata": {},
"source": [
- "### Collections and Structures"
+ "\n",
+ "### Collections and Structures [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "dated-photographer",
"metadata": {},
"source": [
- "#### Lists\n",
+ "\n",
+ "#### Lists [
](#table_of_contents)\n",
"\n",
"YAML includes block collections which use indentation for scope. Here, each entry begins with a new line. Block sequences in collections indicate each entry with a dash and space (`-`). In YAML, block collections styles are not denoted by any specific indicator. Block collection in YAML can distinguished from other scalar quantities with an identification of key value pair included in them.\n",
"\n",
@@ -756,7 +790,6 @@
{
"cell_type": "code",
"execution_count": 58,
- "id": "exciting-repeat",
"metadata": {},
"outputs": [
{
@@ -788,10 +821,10 @@
},
{
"cell_type": "markdown",
- "id": "prescribed-miller",
"metadata": {},
"source": [
- "### Maps\n",
+ "\n",
+ "### Maps [
](#table_of_contents)\n",
"\n",
"Mappings are the representation of key value as included in JSON structure. It is used often in multi-lingual support systems and creation of API in mobile applications. Mappings use key value pair representation with the usage of colon and space (:).\n",
"\n",
@@ -808,7 +841,6 @@
{
"cell_type": "code",
"execution_count": 10,
- "id": "vital-advertiser",
"metadata": {},
"outputs": [
{
@@ -835,10 +867,10 @@
},
{
"cell_type": "markdown",
- "id": "advisory-pottery",
"metadata": {},
"source": [
- "### References\n",
+ "\n",
+ "### References [
](#table_of_contents)\n",
"\n",
"References or anchors are useful when you want to copy an existing structure into a new form without repeating. An example is given below:"
]
@@ -846,7 +878,6 @@
{
"cell_type": "code",
"execution_count": 64,
- "id": "ambient-habitat",
"metadata": {},
"outputs": [
{
@@ -888,10 +919,10 @@
},
{
"cell_type": "markdown",
- "id": "pleased-duncan",
"metadata": {},
"source": [
- "### Multiple documents\n",
+ "\n",
+ "### Multiple documents [
](#table_of_contents)\n",
"\n",
"Three dashes `---`, mark the beginning of a new document"
]
@@ -899,7 +930,6 @@
{
"cell_type": "code",
"execution_count": 28,
- "id": "american-middle",
"metadata": {},
"outputs": [],
"source": [
@@ -915,18 +945,17 @@
},
{
"cell_type": "markdown",
- "id": "contained-gazette",
"metadata": {},
"source": [
- "### Tags\n",
+ "\n",
+ "### Tags [
](#table_of_contents)\n",
"\n",
"Tags, in YAML, are used to declare types. Examples are given below:"
]
},
{
"cell_type": "code",
- "execution_count": 79,
- "id": "young-secretary",
+ "execution_count": 16,
"metadata": {},
"outputs": [
{
@@ -937,7 +966,7 @@
" 'an image': b'GIF89a\\x01\\x00\\x01\\x00\\x00\\x00\\x00!\\xf9\\x04\\x01\\n\\x00\\x01\\x00,\\x00\\x00\\x00\\x00\\x01\\x00\\x01\\x00\\x00\\x02\\x02L\\x01\\x00;'}"
]
},
- "execution_count": 79,
+ "execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
@@ -958,8 +987,7 @@
},
{
"cell_type": "code",
- "execution_count": 33,
- "id": "special-paradise",
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
@@ -968,7 +996,7 @@
"{'integer': '3', 'key': (1, 2)}"
]
},
- "execution_count": 33,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -980,6 +1008,13 @@
"key: !!python/tuple [1, 2]\n",
"\"\"\")"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
@@ -998,7 +1033,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.9"
+ "version": "3.7.6"
}
},
"nbformat": 4,
diff --git a/python/05. Modules/16 pathlib.ipynb b/Python/05. Modules/03 pathlib.ipynb
similarity index 63%
rename from python/05. Modules/16 pathlib.ipynb
rename to Python/05. Modules/03 pathlib.ipynb
index ff42220..7f6e482 100755
--- a/python/05. Modules/16 pathlib.ipynb
+++ b/Python/05. Modules/03 pathlib.ipynb
@@ -2,15 +2,40 @@
"cells": [
{
"cell_type": "markdown",
- "id": "liked-royal",
"metadata": {},
"source": [
- "# `pathlib` Module: Taming the File System"
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#
`pathlib` module: Taming the File System \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "##
Table of Contents \n",
+ "* [The Problem With Python File Path Handling](#the_problem_with_python_file_path_handling)\n",
+ "* [Creating Paths](#creating_paths)\n",
+ "* [Reading and Writing Files](#reading_and_writing_files)\n",
+ "* [Picking Out Components of a Path](#picking_out_components_of_a_path)\n",
+ "* [Moving and Deleting Files](#moving_and_deleting_files)\n",
+ "* [Examples](#examples)\n",
+ " * [Counting Files](#counting_files)\n",
+ "* [Find the Last Modified File](#find_the_last_modified_file)\n",
+ " * [Create a Unique File Name](#create_a_unique_file_name)\n",
+ "* [
Conclusion](#conclusion)\n",
+ "\n",
+ "---"
]
},
{
"cell_type": "markdown",
- "id": "structured-jacket",
"metadata": {},
"source": [
"Have you struggled with file path handling in Python? In Python 3.4 and above, the struggle is now over! You no longer need to scratch your head over code like:\n",
@@ -22,7 +47,6 @@
},
{
"cell_type": "markdown",
- "id": "cellular-galaxy",
"metadata": {},
"source": [
"Or cringe at the verbosity of:\n",
@@ -34,7 +58,6 @@
},
{
"cell_type": "markdown",
- "id": "careful-proportion",
"metadata": {},
"source": [
"In this tutorial, you will see how to work with file paths—names of directories and files—in Python. You will learn new ways to read and write files, manipulate paths and the underlying file system, as well as see some examples of how to list files and iterate over them. Using the pathlib module, the two examples above can be rewritten using elegant, readable, and Pythonic code like:\n",
@@ -47,15 +70,14 @@
},
{
"cell_type": "markdown",
- "id": "constitutional-greene",
"metadata": {},
"source": [
- "## The Problem With Python File Path Handling"
+ "\n",
+ "## The Problem With Python File Path Handling [
](#table_of_contents)"
]
},
{
"cell_type": "markdown",
- "id": "increasing-foster",
"metadata": {},
"source": [
"Working with files and interacting with the file system are important for many different reasons. The simplest cases may involve only reading or writing files, but sometimes more complex tasks are at hand. Maybe you need to list all files in a directory of a given type, find the parent directory of a given file, or create a unique file name that does not already exist."
@@ -63,7 +85,6 @@
},
{
"cell_type": "markdown",
- "id": "inner-crime",
"metadata": {},
"source": [
"Traditionally, Python has represented file paths using regular text strings. With support from the [`os.path`](https://docs.python.org/3/library/os.path.html) standard library, this has been adequate although a bit cumbersome (as the second example in the introduction shows). However, since paths are not strings, important functionality is spread all around the standard library, including libraries like os, glob, and shutil. The following example needs three import statements just to move all text files to an archive directory:"
@@ -71,8 +92,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
- "id": "similar-policy",
+ "execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
@@ -88,7 +108,6 @@
},
{
"cell_type": "markdown",
- "id": "continent-maintenance",
"metadata": {},
"source": [
"With paths represented by strings, it is possible, but usually a bad idea, to use regular string methods. For instance, instead of joining two paths with + like regular strings, you should use `os.path.join()`, which joins paths using the correct path separator on the operating system. Recall that Windows uses `\\` while Mac and Linux use `/` as a separator. This difference can lead to hard-to-spot errors, such as our first example in the introduction working for only Windows paths."
@@ -96,7 +115,6 @@
},
{
"cell_type": "markdown",
- "id": "comparative-muslim",
"metadata": {},
"source": [
"The `pathlib` module was introduced in Python 3.4 (PEP 428) to deal with these challenges. It gathers the necessary functionality in one place and makes it available through methods and properties on an easy-to-use Path object."
@@ -104,7 +122,6 @@
},
{
"cell_type": "markdown",
- "id": "lesbian-surfing",
"metadata": {},
"source": [
"Early on, other packages still used strings for file paths, but as of Python 3.6, the `pathlib` module is supported throughout the standard library, partly due to the addition of a file system path protocol. If you are stuck on legacy Python, there is also a backport available for Python 2."
@@ -112,10 +129,10 @@
},
{
"cell_type": "markdown",
- "id": "sexual-france",
"metadata": {},
"source": [
- "## Creating Paths\n",
+ "\n",
+ "## Creating Paths [
](#table_of_contents)\n",
"\n",
"All you really need to know about is the pathlib.Path class. There are a few different ways of creating a path. First of all, there are classmethods like `.cwd()` (Current Working Directory) and `.home()` (your user’s home directory):"
]
@@ -123,7 +140,6 @@
{
"cell_type": "code",
"execution_count": 8,
- "id": "lonely-vinyl",
"metadata": {},
"outputs": [
{
@@ -145,7 +161,6 @@
},
{
"cell_type": "markdown",
- "id": "noble-reception",
"metadata": {},
"source": [
"> Note: Throughout this tutorial, we will assume that `pathlib` has been imported, without spelling out `import pathlib` as above. As you will mainly be using the `Path` class, you can also do `from pathlib import Path` and write `Path` instead of `pathlib.Path`."
@@ -153,7 +168,6 @@
},
{
"cell_type": "markdown",
- "id": "chemical-trainer",
"metadata": {},
"source": [
"A path can also be explicitly created from its string representation:"
@@ -162,7 +176,6 @@
{
"cell_type": "code",
"execution_count": 12,
- "id": "occasional-japanese",
"metadata": {},
"outputs": [
{
@@ -182,7 +195,6 @@
},
{
"cell_type": "markdown",
- "id": "iraqi-green",
"metadata": {},
"source": [
"A little tip for dealing with Windows paths: on Windows, the path separator is a backslash, `\\`. However, in many contexts, backslash is also used as an escape character in order to represent non-printable characters. To avoid problems, use raw string literals to represent Windows paths. These are string literals that have an `r` prepended to them. In raw string literals the `\\` represents a literal backslash: `r'C:\\Users'`."
@@ -190,16 +202,21 @@
},
{
"cell_type": "markdown",
- "id": "micro-compound",
"metadata": {},
"source": [
"A third way to construct a path is to join the parts of the path using the special operator `/`. The forward slash operator is used independently of the actual path separator on the platform:"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `/` can join several paths or a mix of paths and strings (as above) as long as there is at least one Path object. If you do not like the special `/` notation, you can do the same thing with the `.joinpath()` method:"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 10,
- "id": "likely-williams",
+ "execution_count": 11,
"metadata": {},
"outputs": [
{
@@ -208,469 +225,584 @@
"PosixPath('/home/ali/python/scripts/test.py')"
]
},
- "execution_count": 10,
+ "execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "pathlib.Path.home() / 'python' / 'scripts' / 'test.py'"
+ "pathlib.Path.home().joinpath('python', 'scripts', 'test.py')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "PosixPath('prefix/path/github')"
+ ]
+ },
+ "execution_count": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\"prefix\" / pathlib.Path(\"./path\")"
]
},
{
"cell_type": "markdown",
- "id": "magnetic-burning",
"metadata": {},
"source": [
- "The `/` can join several paths or a mix of paths and strings (as above) as long as there is at least one Path object. If you do not like the special `/` notation, you can do the same thing with the `.joinpath()` method:"
+ "Note that in the preceding examples, the `pathlib.Path` is represented by either a `WindowsPath` or a `PosixPath`. The actual object representing the path depends on the underlying operating system. (That is, the `WindowsPath` example was run on Windows, while the `PosixPath` examples have been run on Mac or Linux.) See the section Operating System Differences for more information."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "## The Anatomy of a PosixPath [
](#table_of_contents)\n",
+ "To make it easier to understand the basics components of a Path, we'll their basic components:"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The different parts of a path are conveniently available as properties. Basic examples include:\n",
+ "\n",
+ "- `.name`: the file name without any directory\n",
+ "- `.parent`: the directory containing the file, or the parent directory if path is a directory\n",
+ "- `.stem`: the file name without the suffix\n",
+ "- `.suffix`: the file extension\n",
+ "- `.anchor`: the part of the path before the directories\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```python\n",
+ ">>> from pathlib import Path\n",
+ "\n",
+ ">>> path = Path('/home/miguel/projects/blog/config.tar.gz')\n",
+ "\n",
+ ">>> path.drive\n",
+ "'/'\n",
+ "\n",
+ ">>> path.root\n",
+ "'/'\n",
+ "\n",
+ ">>> path.anchor\n",
+ "'/'\n",
+ "\n",
+ ">>> path.parent\n",
+ "PosixPath('/home/miguel/projects/blog')\n",
+ "\n",
+ ">>> path.name\n",
+ "'config.tar.gz'\n",
+ "\n",
+ ">>> path.stem\n",
+ "'config.tar'\n",
+ "\n",
+ ">>> path.suffix\n",
+ "'.gz'\n",
+ "\n",
+ ">>> path.suffixes\n",
+ "['.tar', '.gz']\n",
+ "```"
]
},
{
"cell_type": "code",
- "execution_count": 11,
- "id": "sunset-example",
+ "execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "PosixPath('/home/ali/python/scripts/test.py')"
+ "('/', 'home', 'ali', 'CS-Tutorial', 'python', 'modules', 'text.txt')"
]
},
- "execution_count": 11,
+ "execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "pathlib.Path.home().joinpath('python', 'scripts', 'test.py')"
+ "path = Path('/home/ali/CS-Tutorial/python/modules/text.txt')\n",
+ "path.resolve().parts"
]
},
{
"cell_type": "code",
- "execution_count": 14,
- "id": "antique-silence",
+ "execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "PosixPath('prefix/path')"
+ "PosixPath('/home/ali/CS-Tutorial/python/new.txt')"
]
},
- "execution_count": 14,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "\"prefix\" / pathlib.Path(\"./path\")"
+ "path.parent.parent / ('new' + path.suffix)"
]
},
{
"cell_type": "markdown",
- "id": "alien-meter",
"metadata": {},
"source": [
- "Note that in the preceding examples, the `pathlib.Path` is represented by either a `WindowsPath` or a `PosixPath`. The actual object representing the path depends on the underlying operating system. (That is, the `WindowsPath` example was run on Windows, while the `PosixPath` examples have been run on Mac or Linux.) See the section Operating System Differences for more information."
+ "\n",
+ "## Creating Directories with pathlib [
](#table_of_contents)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`pathlib.Path` comes with a method to create new directories named `Path.mkdir()`."
]
},
{
"cell_type": "markdown",
- "id": "continuous-asian",
"metadata": {},
"source": [
- "## Reading and Writing Files\n",
+ "This method takes three arguments:\n",
"\n",
- "Traditionally, the way to read or write a file in Python has been to use the built-in `open()` function. This is still true as the `open()` function can use `Path` objects directly. The following example finds all headers in a Markdown file and prints them:"
+ "- `mode`: Used to determine the file mode and access flags\n",
+ "- `parents`: Similar to the `mkdir -p` command in Unix systems. Default to False which means it raises errors if there's the parent is missing, or if the directory is already created. When it's True, `pathlib.mkdir` creates the missing parent directories.\n",
+ "- `exist_ok`: Defaults to False and raises FileExistsError if the directory being created already exists. When you set it to True, pathlib ignores the error if the last part of the path is not an existing non-directory if file. directory is not empty, pathlib won't override it. lets see some examples :\n",
+ "\n"
]
},
{
- "cell_type": "code",
- "execution_count": 32,
- "id": "contained-corrections",
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
- "path = pathlib.Path.cwd() / 'test.md'\n",
- "with open(path, mode='w') as f:\n",
- " f.write(\"Hello World from pathlib!\")"
+ "```python\n",
+ "# Create a directory that already exists\n",
+ ">>> list(Path.cwd().iterdir())\n",
+ "[PosixPath('/home/miguel/path/not_created_yet'),\n",
+ " PosixPath('/home/miguel/path/reports'),\n",
+ " PosixPath('/home/miguel/path/new_directory')]\n",
+ "\n",
+ ">>> path = Path('new_directory')\n",
+ "\n",
+ ">>> path.exists()\n",
+ "True\n",
+ "\n",
+ ">>> path.mkdir()\n",
+ "---------------------------------------------------------------------------\n",
+ "FileExistsError Traceback (most recent call last)\n",
+ " in \n",
+ "----> 1 path.mkdir()\n",
+ "\n",
+ "~/.pyenv/versions/3.9.4/lib/python3.9/pathlib.py in mkdir(self, mode, parents, exist_ok)\n",
+ " 1311 try:\n",
+ "-> 1312 self._accessor.mkdir(self, mode)\n",
+ " 1313 except FileNotFoundError:\n",
+ " 1314 if not parents or self.parent == self:\n",
+ "\n",
+ "FileExistsError: [Errno 17] File exists: 'new_directory'\n",
+ "\n",
+ ">>> path.mkdir(exist_ok=True)\n",
+ "\n",
+ ">>> list(Path.cwd().iterdir())\n",
+ "[PosixPath('/home/miguel/path/not_created_yet'),\n",
+ " PosixPath('/home/miguel/path/reports'),\n",
+ " PosixPath('/home/miguel/path/new_directory')] \n",
+ "```"
]
},
{
"cell_type": "markdown",
- "id": "excellent-enterprise",
"metadata": {},
"source": [
- "An equivalent alternative is to call `.open()` on the Path object:"
+ "______\n",
+ "```python\n",
+ "# Create a directory that already exists\n",
+ ">>> list(Path.cwd().iterdir())\n",
+ "[PosixPath('/home/miguel/path/not_created_yet'),\n",
+ " PosixPath('/home/miguel/path/reports'),\n",
+ " PosixPath('/home/miguel/path/new_directory')]\n",
+ "\n",
+ ">>> path = Path('new_directory')\n",
+ "\n",
+ ">>> path.exists()\n",
+ "True\n",
+ "\n",
+ ">>> path.mkdir()\n",
+ "---------------------------------------------------------------------------\n",
+ "FileExistsError Traceback (most recent call last)\n",
+ "