diff --git a/README.md b/README.md index fcb348f..89ee154 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,9 @@ # Advanced Python -Examples of advanced Python programming techniques - -## Functions - -* [Function Parameters](functions/function_parameters.md) -* [Variable Scope](functions/scope.md) -* [Lambda Functions](functions/lambda_functions.md) -* [Generator Functions](functions/generators.md) -* [Map-Filter-Reduce](functions/functools.md) -* [Decorators](functions/decorators.md) -* [Exercise](functions/exercises.md) - -## Shortcuts - -* [Collections](shortcuts/collections.md) -* [Comprehensions](shortcuts/comprehensions.md) -* [Enumerations](shortcuts/enums.md) - -## Structuring Code - -* [Structure of a Python script](structure/main_block.md) -* [Command-line Arguments](structure/commandline_args.md) -* [Writing Modules](structure/modules/) -* [Writing Packages](structure/package/) - -## Object-Oriented Programming in Python - -* [Classes](classes/classes.md) -* [Inheritance](classes/inheritance.md) -* [Object Composition](classes/composition.md) -* [Operator Overloading](classes/operator_overloading.md) -* [Abstract Base Classes](classes/abc.md) -* [Decorator Classes](classes/decorator_class.md) -* [Metaclasses](classes/metaclasses.md) - - -## Error Handling - -* [Exception Handling](error_handling/exceptions/) -* [Warnings](error_handling/warnings.md) -* [Logging](error_handling/logging/) - -## Parallel Programming - -* [Concurrency](concurrency/) - -## Challenges - -| Challenge | Difficulty | -|-----------|---------------| -| [Factorial](challenges/factorial.rst) | ⭐ | -| [Sorting Algorithms](challenges/sorting.md) | ⭐ | -| [Tennis](challenges/tennis.md) | ⭐⭐ | -| [Magic Square](challenges/magic_square.md) | ⭐⭐ | -| [Josephus Problem](challenges/josephus.md) | ⭐⭐ | -| [Tree Traversal](challenges/tree_traversal.md) | ⭐⭐ | -| [Maze](challenges/maze.md) | ⭐⭐ | -| [Backpack Problem](challenges/backpack_problem.md) | ⭐⭐⭐ | -| [Chained List](challenges/chained_list.md) | ⭐⭐ | -| [Traveling Salesman](challenges/tsp.md) | ⭐⭐⭐ | -| [Decorator-Metaclass](challenges/metaclass.md) | ⭐⭐⭐⭐⭐ | +Examples and Exercises for advanced Python programming techniques +The Live Website is available at [http://www.academis.eu/advanced_python](http://www.academis.eu/advanced_python) ## Source diff --git a/classes/class_diagram.md b/classes/class_diagram.md new file mode 100644 index 0000000..a208c33 --- /dev/null +++ b/classes/class_diagram.md @@ -0,0 +1,147 @@ + +# Class Diagrams + +One of the first and most important things converting ideas and into code is to structure data. +You want to start structuring your core business data. +In the case of a snake game, this means how the playing field, the snake and the food items are represented. + +Class diagrams are a graphical tool to structure your data and check whether it is complete and non-redundant before writing code. + +## What does a class diagram contain? + +Here is a class diagram for a `PlayingField` class, the box in which a snake will move: + +![class diagram for the PlayingField](images/class_playing_field.png) + +On top, the class diagram contains a **title**, the name of the class in `SnakeCase` notation. + +The second section lists the **attributes** of the class and their **data types**: + +* the x/y size of the playing field, a tuple of two integers +* the x/y position of the food item, also a tuple of two integers + +The third section lists the **methods** of the class with their **arguments** and **return types**: + +* the `add_food()` method takes two integer arguments and returns nothing +* the `add_random_food()` method has no arguments and returns nothing +* the `get_walls()` method takes no arguments and returns a list of x/y integer tuples + +## What the PlayingFiled does not contain + +It is worth pointing out that the `PlayingField` class lacks two things on purpose: + +First, it does not contain an attribute `snake`. +To be precise, it does not know that snakes even exist. +It does not have to know about it. +We want the `PlayingField` and the `Snake` to manage themselves as independently as possible. +This will make debugging a lot easier. + +Second, there is no method `draw()`. +Drawing things is usually not part of your core business. +In the snake game, you may want to change the user interface later (e.g. by adding graphics and sound effects). +The core logic of how the snake moves should not change because of that. + +---- + +## Write Skeleton Code + +A great thing about class diagrams is that you can create them to code easily. +The Python `dataclasses` module saves you a lot of typing: + + from dataclasses import dataclass + + @dataclass + class PlayingField: + + size: (int, int) + food: (int, int) = None + + def add_food(self, x, y): + ... + + def add_random_food(self): + ... + + def get_walls(self): + ... + +This code defines the `size` and `food` attributes and annotates their data types. +The `food` attribute has a default value. +The class also defines the methods from the class diagram (each with the obligatory `self`). +But we leave the method bodies empty for now. + +The `@dataclass` automatically creates the `__init__()` and `__repr__()` methods for you, so that you can set and inspect the attribute values. +The code is already executable: + + pf = PlayingField(size=(10, 10)) + print(pf) + print(pf.size) + print(pf.get_walls()) + +Although our class does nothing yet, it helps to think about your desing and write other code that depends on it. + +---- + +## Alternative Designs + +Usually, there is more than one way to design a class. +Consider this alternative design for `PlayingField`: + +![alternative PlayingField class](images/class_playing_field_alt.png) + +There are a few differences: + +* size and food have separate x and y attributes instead of being tuples +* the walls are represented by a list of `(int, int)` tuples +* the `add_food()` method expects a tuple instead of two integers +* there methods `is_wall()` and `get_walls()` are no longer there + +One could discuss a lot which design is better. +You are better off postponing that discussion to a cleanup stage once the code is running. +The differences are very small and easy to change. +In Python, one could even state that the data structures are practically *identical*. + +Using the `@property` decorator, you can translate attributes into each other. +The following code translates the `size` attribute into two new attributes `size_x` and `size_y`: + + @property + def size_x(self): + return self.size[0] + + @property + def size_y(self): + return self.size[1] + +Now you can use all three attributes without storing redundant data: + + pf = PlayingField(size=(5, 5)) + print(pf.size) + print(pf.size_x) + print(pf.size_y) + + +More complex and difficult questions arise when planning relationships between multiple classes. +There will be multiple working alternatives, but some may fall on your feet in the long run. +You may want to read more about **SOLID principles**, **Object Composition** and **Design Patterns**. + +## Classes vs SQL + +If you have worked with SQL, there is a striking parallel between SQL tables and classes. +Tables have columns, classes have attributes. +Tables have rows, classes have instances. +Both structure data. +Class diagrams are conceptually very close to Entity-Relationship (ER) diagrams used in the database world. + +---- + +## Exercise + +Turn the class diagram of the Snake class into skeleton code. +Leave all methods empty. + +![Snake class diagram](images/class_snake.png) + +---- +## Further Reading + +The class diagrams in this article were designed with the online tool [Creately](https://app.creately.com). diff --git a/conf.py b/conf.py index a4dc644..284ac4e 100644 --- a/conf.py +++ b/conf.py @@ -22,7 +22,7 @@ ] templates_path = ['_templates'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'README.md'] language = 'ls' diff --git a/getting_started/create_repo.png b/getting_started/create_repo.png new file mode 100644 index 0000000..1ee847b Binary files /dev/null and b/getting_started/create_repo.png differ diff --git a/getting_started/git_dialog.png b/getting_started/git_dialog.png new file mode 100644 index 0000000..3d132f8 Binary files /dev/null and b/getting_started/git_dialog.png differ diff --git a/getting_started/git_repo.rst b/getting_started/git_repo.rst new file mode 100644 index 0000000..9560d5e --- /dev/null +++ b/getting_started/git_repo.rst @@ -0,0 +1,172 @@ +Set up a gitHub Repository +========================== + +Version Control is in my opinion the single most important tool of +modern software development. Most of the time, using Version Control +means using **git**. In this recipe, you will set up your own project +repository on `GitHub `__. + +I won’t be describing here what git is or how to use it. There are lots +of high quality tutorials for it. If you have never used git before or +need a refresher, check out the `Introduction to Git and +GitHub `__ by Jim +Anderson. + +Let’s go through the steps setting git/GitHub for your project: + +Step 1: Create a repository +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are two ways to create a new repository: + +1. using the GitHub website +2. using ``git init`` in the terminal + +I recommend starting on GitHub, because you can create a couple of +useful files there right away. + +Log in to your account on `github.com `__ (or +create one if you are there for the first time). Then, find the **button +with a big plus (+)** in the top right corner and select **New +Repository**. You should see a dialog where you can enter the name and +description of your project: + +.. figure:: create_repo.png + :alt: create a repository + +I recommend you use names in ``lowercase_with_underscores``. This may +avoid random bugs on some operating systems. + +Step 2: Decide whether your project is public +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you scroll down the creation dialog, there are several control +boxes. The first one selects whether your project is visible to the rest +of the world. There is a simple guideline: + +- If your project is work for a company or you feel you want to keep it + confidential, choose **Private**. +- If you would like to share it, choose **Public**. + +It is fine to have public projects that are *incomplete*, *simple* or +*not practically useful*. Having a simple project that is cleaned up +well may be a good advertisement. The Private/Public setting is easy to +change later on as well. + +Step 3: Add a README file +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is a no-brainer. Tick this box. + +Step 4: Add a .gitignore file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``.gitignore`` file prevents that many types of temporary or +auto-generated files are added to your repository. It is another +must-have. Choose **Python** from the dialog. + +Step 5: Choose a license +~~~~~~~~~~~~~~~~~~~~~~~~ + +GitHub offers a selection of time-tested open-source licenses. They are +legally watertight, but differ in subtle aspects. For a hobby project, I +recommend the **MIT License**. It roughly says: + +- other people may use your code for whatever they want +- they have to keep the MIT License in +- you cannot be held legally liable + +If you find out later that you need a different license, you as the +author are allowed to change it. + +Step 6: Create the repo +~~~~~~~~~~~~~~~~~~~~~~~ + +Now you are ready to go. The dialog should look similar to this: + +.. figure:: git_dialog.png + :alt: Git setup dialog + +Press the **Create Repository** button. After a few seconds, you should +see a page listing the files in your new project repo: + +:: + + .gitignore + LICENSE + README.md + +Step 7: Clone the repository +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Next, create a local working copy of your project. For that, you need to +**clone** the repository. You need to have git installed on your +computer. You find a `git installation guide on +git-scm.com `__. + +To clone the repository, you need the address under the big button +labeled **Code**: + +.. figure:: git_url.png + :alt: Copy the URL + +Do the following: + +1. Copy the address starting with ``git@github..`` from the GitHub page +2. Open a terminal on your computer +3. Use ``cd`` to navigate to the folder where you keep your projects +4. Type ``git clone`` followed by the address you just copied + +You should see a message similar to: + +:: + + kristian@mylaptop:~/projects$ git clone git@github.com:krother/snake.git + Cloning into 'snake'... + remote: Enumerating objects: 5, done. + remote: Counting objects: 100% (5/5), done. + remote: Compressing objects: 100% (4/4), done. + Receiving objects: 100% (5/5), done. + remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 + +There also should be a new folder: + +:: + + kristian@mylaptop:~/projects$ ls -la snake + total 24 + drwxrwxr-x 3 kristian kristian 4096 Mai 28 11:33 . + drwxrwxr-x 50 kristian kristian 4096 Mai 28 11:33 .. + drwxrwxr-x 8 kristian kristian 4096 Mai 28 11:33 .git + -rw-rw-r-- 1 kristian kristian 1799 Mai 28 11:33 .gitignore + -rw-rw-r-- 1 kristian kristian 1072 Mai 28 11:33 LICENSE + -rw-rw-r-- 1 kristian kristian 35 Mai 28 11:33 README.md + +Step 8: Add your code +~~~~~~~~~~~~~~~~~~~~~ + +Now you can start adding code to your repository. For instance you could +add a prototype if you have one. The sequence of commands might look +like this: + +:: + + cd snake/ + cp ~/Desktop/prototype.py . + git status + git add prototype.py + git commit -m "add a snake prototype" + git push + +To exectute ``git push``, you may need to `Add SSH keys to your GitHub +account `__. + +In the end, you should see the code of your prototype on your GitHub +page. **Congratulations!** + + +.. seealso:: + + - `Git Introduction `__ + - `Try GitHub - Online-Tutorial `__ + - `Pro Git `__ – the book by Scott Chacon diff --git a/getting_started/git_url.png b/getting_started/git_url.png new file mode 100644 index 0000000..e49172f Binary files /dev/null and b/getting_started/git_url.png differ diff --git a/getting_started/structure.rst b/getting_started/structure.rst new file mode 100644 index 0000000..6f3464e --- /dev/null +++ b/getting_started/structure.rst @@ -0,0 +1,107 @@ +Create a Folder Structure +========================= + +A small but important part of a project is creating folders for your +code. In the Python world, there is a standard structure that you will +find in many other projects. The next few steps let you create one. + +Step 1: A package folder +~~~~~~~~~~~~~~~~~~~~~~~~ + +First, you need a place for the package you want to write. A **Python +package** is simply a folder that contains ``.py`` files. Create a +folder ``snake`` inside your repository. On the bash terminal, you would +use + +:: + + mkdir snake + +If your git repository is also called ``snake``, you may want to rename +your project folder to something else like ``snake_project``, +``snake_repo`` or similar. If you have two folders calles ``snake`` +inside each other could lead to strange import bugs later + +-------------- + +Step 2: A folder for tests +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You will also want to have a place where you add test code later. Name +that folder ``tests/``. We will leave it empty for now. + +:: + + mkdir tests + +-------------- + +Step 3: Create a Python module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You may want to create a Python module (a ``.py`` file) to make sure +everything is set up correctly. Create a file ``game.py`` inside the +``snake/`` folder. Add a placeholder function to it: + +:: + + def play_snake(): + print('this is a snake game') + +Now start Python in your main project folder (above the package) through +the terminal. **It is important that you start Python in your project +folder. It will probably not work from your IDE at this point.** The +code that you want to get running is: + +:: + + from snake.game import play_snake + + play_snake() + +You should see the message from the print statement. + +-------------- + +Step 4: main Python file +~~~~~~~~~~~~~~~~~~~~~~~~ + +Importing the ``play_snake()`` function to play the game is a bit +inconvenient. Let’s create a shortcut. Create a file named +``__main__.py`` (with double underscores on both ends) in the package +folder that contains the following code: + +:: + + from game import play_snake + + play_snake() + +Now it should be possible to start the game by typing: + +:: + + python snake + +-------------- + +Summary +~~~~~~~ + +At this point, your project folder should contain: + +:: + + LICENSE + prototype.py + README.md + snake/ + game.py + __main__.py + tests/ + + +.. seealso:: + + You find detailed info on importing stuff in + `Python Modules and Packages on realpython.com `__ diff --git a/getting_started/virtualenv.rst b/getting_started/virtualenv.rst new file mode 100644 index 0000000..6377538 --- /dev/null +++ b/getting_started/virtualenv.rst @@ -0,0 +1,90 @@ +Virtual Environments +==================== + +When developing software, you often need a specific combination of +Python libraries. Sometimes this is difficult, because you require a +specific version of a library, want to test your program on multiple +Python versions, or simply need to develop your program further, while a +stable version is installed on the same machine. In these cases, +**virtual environments** come to the rescue. + +-------------- + +What is a virtual environment? +------------------------------ + +A virtual environment manages multiple parallel installations of Python +interpreters and libraries, so that you can switch between them. The +virtual environment consists of a folder per project, in which Python +libraries for that project are installed. + +-------------- + +How to install a virtual environment? +------------------------------------- + +There are many Python tools to manage virtual environments: venv, +virtualenv, Pipenv and Poetry. A beginner-friendly tool is to use +**conda**. If you haven’t installed Anaconda already, you can find the +**Miniconda installer** at https://conda.io/miniconda.html. + +-------------- + +How to set up a project with conda? +----------------------------------- + +Once the installer finishes and you open a new terminal, you should see +``(base)`` before the prompt: + +:: + + (base) ada@adas_laptop:~$ + +This means you are in an virtual environment called *“base”*. + +Let’s create a new one for a project called **snake**, specifying a +Python version: + +:: + + conda create -n snake python=3.11 + +Behind the scenes **conda** creates a new subdirectory. This is where +libraries for your project will be stored. There are also scripts to +activate the environment. + +-------------- + +How to work with an environment +------------------------------- + +To start working with your project, type: + +:: + + conda activate snake + +You should see a *(snake)* appearing at your prompt. Now, whenever you +use *pip* to install something, it will be installed only for +*myproject*. + +Now check which libraries you have installed: + +:: + + pip freeze + +You can install additional libraries with ``pip`` or ``conda``: + +:: + + conda install pandas + +When you want to switch back to the base environment, type: + +:: + + conda activate base + +The virtual environment is specific for a terminal session. Thus, you +can work on as many projects simultaneously as you have terminals open. diff --git a/index.rst b/index.rst index 2855eee..102ce08 100644 --- a/index.rst +++ b/index.rst @@ -3,6 +3,17 @@ Advanced Python Examples of advanced Python programming techniques +Setting up a Python Project +--------------------------- + +.. toctree:: + :maxdepth: 1 + + getting_started/structure.rst + getting_started/virtualenv.rst + getting_started/git_repo.rst + + Functions --------- @@ -26,8 +37,8 @@ Shortcuts shortcuts/comprehensions.rst shortcuts/enums.rst -Structuring Code ----------------- +Structuring Programs +-------------------- .. toctree:: :maxdepth: 1 @@ -36,8 +47,8 @@ Structuring Code structure/commandline_args.rst structure/modules.rst -Object-Oriented Programming in Python -------------------------------------- +Object-Oriented Programming +--------------------------- .. toctree:: :maxdepth: 1 @@ -45,6 +56,7 @@ Object-Oriented Programming in Python classes/classes.rst classes/inheritance.rst classes/composition.rst + classes/class_diagram.md classes/operator_overloading.rst classes/abc.rst classes/decorator_class.rst