{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(hi-python)=\n", "# Hello Data Types\n", "\n", "About Python, variable types and script execution. For interactive reading and executing code blocks [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/hydro-informatics/jupyter-python-course/main) and find *b01-pybase.ipynb*, or install {ref}`Python ` and {ref}`JupyterLab ` locally.\n", "\n", "```{admonition} Requirements for Local Usage\n", "For running code locally, {ref}`install Python ` along with an {ref}`IDE ` that fits your needs and preferences.\n", "```\n", "\n", "```{admonition} Watch this section as a video\n", ":class: tip, dropdown\n", "\n", "

Watch this section as a video on the @Hydro-Morphodynamics channel on YouTube.

\n", "```\n", "\n", "## Load Python Environment\n", "\n", "These pages are written with [*JupyterLab*](https://jupyter.org/) . For the best learning experience make sure to read about {ref}`sec-ide` before starting this tutorial.\n", "\n", "## The First Lines of Code\n", "\n", "The examples in the first steps revolve around ice cream to illustrate the utility of simple code snippets. If you do not like ice cream, replace it with your favorite category of things.\n", "\n", "```{admonition} How to Run Python\n", ":class: tip\n", "\n", "The very first thing you need to run Python code is, obviously, Python itself. Python can be started in a standard (system) terminal or a terminal of an IDE. Make sure to use a standard terminal for your platform according to the {ref}`Python installation ` descriptions. For instance:\n", "\n", "* On Windows, click on the start menu, tap `Anaconda Prompt`, and hit `Enter`\n", "* On Linux, open the system Terminal (e.g., Gnome Terminal by pressing `Ctrl` + `Alt` + `T` - if nothing happens, have a look at the {ref}`Linux Terminal introduction `)\n", "* On macOS, find the Terminal (or *Command Line Tools*), tap `Python`, and hit `Enter`\n", "\n", "**Alternatively**, **use your favorite IDE** to run Python code in a terminal environment:\n", "\n", "* With **PyCharm** (Windows):\n", " * Create a new empty project and use the *flusstools* environment (read {ref}`how to get it ` and {ref}`how to create a PyCharm project `)\n", " * Find and click on the **Python Console** tab at the bottom of the PyCharm window\n", " * The Python console is now ready to use.\n", "* With **Jupyter** (cross-platform):\n", " * Open a Terminal following the above descriptions\n", " * Enter `jupyter-lab` (or `jupyter lab`, or `jupyterlab`, or `jupyter notebook` depending on your installation) > Jupyter opens in your web browser\n", " * Navigate to a local copy of this Jupyter notebook and run any code block (click on a code block and hit the run triangle button in the top menu).\n", "```\n", "\n", "Ultimately, **you are ready to** get the very first \"feedback\" (*callback*) from the **Python** console. Start with a simple call of the `print` function by entering:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"This is callback from the digital ice cream printer.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall that the `print` command, like all other Python commands that will follow, can be either run in a *Python* console (e.g., *Conda Prompt* on Windows or *Terminal* on Linux) or in your favorite IDE. In addition, we can write down the `print` command in a **Python file ending on `.py`** (e.g., `icecream_tutorial.py`) and run the file. For instance, in **PyCharm**:\n", "\n", "* Expand the project branch (if not yet done: on the left side of the window)\n", "* Right-click in the project folder, select `New` > `Python File` and name the new *Python* (`.py`) file (e.g., `icecream_tutorial.py`). \n", "* Copy the above `print(\"...\")` code into the new Python file and save it.\n", "* Right-click in the Python file, then `Run icecream_tutorial.py` (alternatively: press the `Ctrl` + `Shift` + `F10` keys).\n", "* Now, the *Python Console* should pop up at the bottom of the window and it will print the text in the above `print` command.\n", "\n", "In any **other (IDE) Terminal**, `cd` to the directory where the Python file (`icecream_tutorial.py`) lives, then enter `python icecream_tutorial.py` and hit `Enter`.\n", "\n", "```{note}\n", "In the following, there will not be any more differentiation between using PyCharm or any other IDE and Jupyter. Thus, **Run the script/code block** means: run the script in your favorite IDE in the following.\n", "```\n", "\n", "With the `\"` apostrophes in the `print` command, we pass a ***string*** variable to the `print` command. Instead of using `\"`, one can also use `'`, but it is important to use the same type of apostrophe at the beginning and the end of the string (text) variable. \n", "\n", "```{warning}\n", "Be reasonable with the usage of `print`. Especially in loops, `print` leads to unnecessary system load and slows down the script.\n", "```\n", "\n", "\n", "A marginal note: In Python3 `print` is a function, not a keyword as in Python2. `print` is useful, for example, to make a script show where it is currently running. It is also possible to print other types of variables than strings, but the combination of numerical and text variables requires more encoding (see next sections). \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(var)=\n", "## Python Variables and Data Types\n", "\n", "The above-shown `print` function already involved *string* variables. In addition, there are a couple of other variable (data) types in Python:\n", "\n", " - text\n", " - boolean\n", " - number (numeric)\n", " - tuple\n", " - list\n", " - dictionary\n", " \n", "```{tip}\n", "Data and variable types are difficult to understand if you simply try to learn them by heart. Remember that different data types can be useful when writing code for a particular solution.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Text\n", "\n", "Text variables in *Python* are *strings* (`str`) that consist of multiple *characters* (`chr`):\n", "\n", "`````{tab-set}\n", "````{tab-item} Rendered table\n", "| Type | Example | Description |\n", "|------|:-----------:|------------------------------------|\n", "| str | `\"apple\"` | String embraced with double quotes |\n", "| | `'apple'` | String embraced with single quotes |\n", "| | `\"\"\"apple\"\"\"` | Literal string (multi-line text) |\n", "| chr | `\"a\"` | Character (unit of a string) |\n", "\n", "````\n", "\n", "````{tab-item} Source code\n", "```\n", "| Type | Example | Description |\n", "|------|:-----------:|------------------------------------|\n", "| str | `\"apple\"` | String embraced with double quotes |\n", "| | `'apple'` | String embraced with single quotes |\n", "| | `\"\"\"apple\"\"\"` | Literal string (multi-line text) |\n", "| chr | `\"a\"` | Character (unit of a string) |\n", "\n", "```\n", "````\n", "`````\n", "\n", "\n", "In addition, string variables have some built-in functions that facilitate coding. To create (instantiate) a string variable, use the `=` sign as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "flavor1 = \"vanilla\" # str\n", "first_letter = \"v\" # str / char\n", "print(flavor1.upper())\n", "print(flavor1[0])\n", "print(flavor1.split(\"ll\")[0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Characters can be converted to numbers and the other way round. This feature can be useful in loops to iterate for example over alphabetically ordered lists (attention: the conversion depends on how your operating system is configured)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(chr(67))\n", "print(int(\"c\", 36)) # use int(letter, literal)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(bool)=\n", "### Boolean\n", "\n", "Boolean variables are either `True` (1) or `False` (0) with many useful code implementations. We will come back to booelans (**bool**) later in the section on conditional (`if ...`) statements." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bowl = False\n", "print(\"The bowl exists: \" + str(bowl))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(num)=\n", "### Numbers (Numeric)\n", "\n", "Python knows a couple of different numeric data types:\n", "\n", "| Type | Example | Description |\n", "|---------|:-------:|----------------------------------------------------------|\n", "| `int` | `10` | Signed Integer |\n", "| `float` | `5.382` | Floating point real number |\n", "| `complex` | `1.43J` | Complex number where J is in the range between 0 and 255 |\n", "\n", "To create a numeric variable, use the `=` sign as follows:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "scoops = 2 # int\n", "weight = 0.453 # float" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Python** does not require a type assignment for a variable because it **is a high-level, interpreted programming language** (other than, for example, *C++*). However, once a variable was assigned a data type, do not change it in the code (it is just good practice - so that scoops remain integers)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If a print statement combines numeric and text variables, the numeric variables first have to be converted to text, and then **concatenated** to a string. There are several ways to combine multiple variables in a text string:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"My ice cream consists of %d scoops.\" % scoops) # use %d for integers, %f for floats and %s for strings\n", "print(\"My ice cream weighs %1.3f kg.\" % weight)\n", "print(\"My ice cream weighs \" + str(weight) + \" kg.\")\n", "print(\"My ice cream weighs {0} kg and has {1} scoops\".format(weight * scoops, scoops)) # multiple variable conversion" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"My ice cream weighs \" + weight + \" kg.\") # this cannot work because weight is a float" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(list)=\n", "### List \n", "\n", "A list is a series of values, which is embraced with brackets `[]`. The values can be any other data type (i.e., numeric, text, dictionary, or tuple), or even another list (so-called *nested lists*)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "flavors = [\"chocolate\", \"bread\", flavor1] # a list of strings\n", "nested_list = [[1, 2, 3], [\"a\", \"b\", \"c\"]]\n", "print(nested_list)\n", "print(\"A list of strings: \" + str(list(\"ABC\")))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The items of a list are called **entries** and *entries* can be appended, inserted, or deleted from a list.\n", "\n", "```{note}\n", "Python always starts counting from zero. Thus, the first entry of a list is entry number 0.\n", "```\n", "\n", "Also lists have many useful built-in functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "flavors.append(\"cherry\") # append an entry at the end\n", "print(flavors)\n", "flavors.insert(0, \"lemon\") # insert an entry at position 0\n", "print(flavors)\n", "print(\"There are %d flavors in my list.\" % flavors.__len__())\n", "print(*flavors) # print all elements in list - dows not work in combination with str\n", "print(\"This is all I have: \" + str(flavors[:]).strip(\"[]\"))\n", "flavors.__delitem__(2) # bread is not a flavor, so let's remove it\n", "print(\"This is all I have: \" + \", \".join(flavors)) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(tuple)=\n", "### Tuple \n", "\n", "A tuple represents a collection of Python objects, similar to a list, and the sequence of values (data types) in a tuple can take any type. Elements of a tuple are also indexed with integers. In contrast to lists, a tuple is embraced with round parentheses `()` and a **tuple is immutable** while **lists are mutable**. This means that a tuple object can no longer be modified after it has been created. So why would you like to use tuples then? The answer is that a tuple is more memory efficient than a mutable object because the immutable tuple can create references to existing objects. In addition, a tuple can serve as a `key` of a dictionary (see below), which is not possible with a list." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a_tuple = (\"a text element\", 1, 3.03) # example tuple\n", "print(a_tuple[0])\n", "print(a_tuple[-1]) # last element of a tuple (this also works with lists ..)\n", "\n", "# comparison of lists and tuples\n", "import time # we need this package (module here) and we will learn more about modules later\n", "print(\"patience ...\")\n", "\n", "# iterate over a list with 100000 elements\n", "start_time = time.perf_counter()\n", "a_list = [] # empty list\n", "x = range(100000)\n", "for item in x: a_list.append(item)\n", "print(\"Run time with list: \" + str(time.perf_counter() - start_time) + \" seconds.\")\n", "\n", "# iterate over a tuple with 100000 elements with modifying the tuple\n", "start_time = time.perf_counter()\n", "new_tuple = () # empty tuple\n", "x = range(100000)\n", "for item in x: new_tuple = new_tuple + (item,)\n", "print(\"Run time with tuple modification: \" + str(time.perf_counter() - start_time) + \" seconds.\")\n", "\n", "# iterate over a tuple with 100000 elements if no modification of the tuple is needed\n", "start_time = time.perf_counter()\n", "new_tuple = tuple(range(100000)) \n", "for item in new_tuple: pass\n", "print(\"Run time without tuple modification: \" + str(time.perf_counter() - start_time) + \" seconds.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(dict)=\n", "### Dictionary \n", "\n", "Dictionaries are a powerful data type in Python and have the basic structure `my_dict = {key: value}`. In contrast to lists, an element of a dictionary is called by invoking a `key` rather than an entry number. A dictionary is not enumerated and `key`s just point to their `value`s (whatever data type the `value` is)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict = {1: \"Value 1\", 2: \"Value 2\"}\n", "another_dict = {\"list1\": [1, 2, 3], \"number2\": 1}\n", "my_dict [1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also dictionaries have many useful built-in functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "my_dict.update({3: \"Value 3\"}) # add a dictionary element \n", "my_dict\n", "my_dict.__delitem__(1) # delete a dictionary element \n", "my_dict.__len__() # get the length (number of dictionary elements)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, two lists of the same length can be *zipped* into a dictionary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "weight = [0.5, 1.0, 1.5, 2.0]\n", "price = [1, 1.5, 1.8, 2.0]\n", "apple_weight_price = dict(zip(weight, price))\n", "print(\"{0} kg apples cost EUR {1}.\".format(weight[2], apple_weight_price[weight[2]]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(pyset)=\n", "### Sets\n", "\n", "A Python set is a mutable object that is instantiated with curly brackets. Sets are unordered (in contrast to lists and tuples), and therefore, useful for mathematical operations, such as the union, intersection, difference, or symmetric difference of sets of elements. The below code block features the instantiation of two sets and the application of mathematical operators." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ice shops united: {'blue', 'green', 'vanilla', 'chocolate'}\n", "Both shops intersectedly offer: {'chocolate', 'vanilla'}\n", "This is what shop a has, but not shop b: {'green'}\n", "This is what shop b has, but not shop a: {'blue'}\n", "This is what is unique to both shops (symmetric difference): {'green', 'blue'}\n" ] } ], "source": [ "ice_shop_a = {\"vanilla\", \"chocolate\", \"green\"}\n", "ice_shop_b = {\"blue\", \"chocolate\", \"vanilla\"}\n", "\n", "print(\"Ice shops united: \" + str(ice_shop_a | ice_shop_b))\n", "print(\"Both shops intersectedly offer: \" + str(ice_shop_a & ice_shop_b))\n", "print(\"This is what shop a has, but not shop b: \" + str(ice_shop_a - ice_shop_b))\n", "print(\"This is what shop b has, but not shop a: \" + str(ice_shop_b.difference(ice_shop_a)))\n", "print(\"This is what is unique to both shops (symmetric difference): \" + str(ice_shop_a ^ ice_shop_b))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{important} How to initialize an empty set\n", "\n", "Writing `new_variable = {}` instantiates an empty **dictionary**, not a set. Thus, to initialize an empty set, write `new_set = set()`.\n", "```\n", "\n", "```{admonition} Frozensets\n", ":class: tip\n", "\n", "A frozenset can be created with `frozenset(set)` and has the characteristics of a set, but its elements cannot be modified once assigned. Similar to tuples that are immutable lists, frozensets are immutable sets. Why can it make sense to use a frozenset? Because a set is mutable and unhashable, which is why it cannot be used as dictionary keys as opposed to frozensets that are hashable and can be used as keys to a dictionary.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(op)=\n", "## Operators \n", "\n", "The following operators compare data types and output boolean values (`True`or `False`): \n", "\n", "* `a == b` or `a is b` *a* equals / is *b*\n", "* `a and b` *a* and *b*\n", "* `a or b` *a* or *b*\n", "* `a <= b` *a* smaller than or equal to *b* (similar without equal sign)\n", "* `a >= b` *a* larger than or equal to *b* (similar without equal sign)\n", "* `a in b` *a* in *b* (meaningful in stings - see example below)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(not False)\n", "print(1 is 1) # is\n", "print(1 is 2)\n", "print(\"ice\" in \"ice cream\") " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.10.12" } }, "nbformat": 4, "nbformat_minor": 4 }