diff --git a/class_diagram.md b/class_diagram.md deleted file mode 100644 index a208c33..0000000 --- a/class_diagram.md +++ /dev/null @@ -1,147 +0,0 @@ - -# 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/crc_cards.md b/crc_cards.md deleted file mode 100644 index 0cede72..0000000 --- a/crc_cards.md +++ /dev/null @@ -1,99 +0,0 @@ - -# CRC Cards - -CRC stands for **Class-Responsibility-Collaboration**. -In brief, CRC cards help you to build a better object-oriented architecture for your program. -This article explains: - -1. how CRC cards work -2. what they are good for -3. pros and cons of CRC cards. - ----- - -## How do CRC cards work? - -Imagine a small Pacman game. You move a hungry yellow smiley through a maze, eat lots -of dots while a bunch of ghosts is chasing you. The game is implemented with a separate -class for the ghosts in a code module called pac_sprites. The CRC card describes what -the Ghost class is responsible for and what it needs to do its job. - -![CRC Card](images/crc.png) - -On top of the CRC card there is the full class name: `pac_sprites.Ghost`. - -Most of the card is divided into a left and right half. On the left side, responsibilities are -written; this is what the Ghost class does: Displaying a ghost and constantly moving the -ghost on a random path. On the right side, collaborations are listed: all other program -components that the Ghost needs to work. In this example, the Ghost needs a Sprite class -to display itself, it needs a TileFactory to load graphics, and a TiledMap where it moves on. -With this, the CRC card for the Ghost is complete. - -In the same way, you can write CRC cards for many components of a program. They don't -necessarily need to be classes, you can create CRC cards for modules, packages, -libraries, or however organizational units in your favorite language are called. Also you can -write important files into the collaboration column. Taken together, a CRC card consists of -a name of a program component, a list of responsibilities on the left side, and a list of -collaborating components on the right side. - ----- - -## What are CRC cards good for? - -CRC cards are useful in two situations: - -The first situation is when you are designing the architecture for a new program. You -already have detailed knowledge what the program should do, but you need to decide how -the work will be divided up among different program components. The CRC cards help you -to try different possibilities on paper before implementing them. For example, you might -have the idea that instead of a Ghost class you want a GhostManager class that takes -care of drawing and moving all the ghosts. The CRC cards allow you to compare both -versions and discuss their strengths and weaknesses. - -The second situation is when you need to refactor messy parts of a program. When you -have lots of code that need improvement, but you don't know exactly what it does, you can -write CRC cards to document your progress of understanding the code. When you read -the cards, it will be easier for you to notice classes that don't really do much, or -responsibilities that are redundant. For example, if you find a GhostMover class in addition -to the Ghost, and both are responsible for moving ghosts, then it may be worth to get rid of -one of them. - -Taken together, CRC cards can be used to design an architecture and to clean up an -existing program. - ----- - -## What are advantages and disadvantages of CRC cards? - -On the positive side, using CRC cards is easy and it doesn't take much time to write them, -e.g. on A6 paper cards. Such cards can be rearranged easily during a discussion. CRC -cards facilitate teams to focus on object oriented programming and allow everybody to -contribute to the design. Also, CRC cards do not need to be complete to have value (as -opposed to detailed specifications, where omissions can have severe consequences). If -you decide that you need to focus on a few classes and draw CRC cards just for them, -that is fine. On the negative side, the cards lack detail and precision. There is no place to -explain how a class may actually work (expect for maybe mentioning a Design Pattern). -Also, there is absolutely no procedural information like in what sequence events are to -take place. - -Now, why to use CRC cards instead of a class diagram? I agree that both CRC cards and -UML class diagrams describe the architecture of a software. But, the class diagram is a -very precise technical blueprint. If you already know exactly what you need, the class -diagram is great. But if you are thinking about what classes there could possibly be, and -dont want to worry about relationships, methods, or attributes yet, CRC cards offer a less -final method that leaves you freedom for designing details later. The CRC card describes -what a class is good for, which the class diagram doesn't. You can use CRC cards as a -stepping stone in the early stages of developing your class architecture. - ----- - -## Summary -CRC cards describe the architecture of a software. They describe components like classes -or packages, their responsibilities, and collaborations they need to work correctly. CRC -cards are easy to use to design a new architecture or clean up an existing one. - ----- - -## Source - -CRC cards were first proposed by Ward Cunningham and Kent Beck. diff --git a/folders.md b/folders.md deleted file mode 100644 index e3904e6..0000000 --- a/folders.md +++ /dev/null @@ -1,87 +0,0 @@ - -# 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/ - ----- - -## Further Reading - -You find detailed info on importing stuff in -[Python Modules and Packages on realpython.com](https://realpython.com/python-modules-packages/) - diff --git a/images/class_playing_field.png b/images/class_playing_field.png deleted file mode 100644 index c016180..0000000 Binary files a/images/class_playing_field.png and /dev/null differ diff --git a/images/class_playing_field_alt.png b/images/class_playing_field_alt.png deleted file mode 100644 index 6723091..0000000 Binary files a/images/class_playing_field_alt.png and /dev/null differ diff --git a/images/class_snake.png b/images/class_snake.png deleted file mode 100644 index b292fd3..0000000 Binary files a/images/class_snake.png and /dev/null differ diff --git a/images/create_repo.png b/images/create_repo.png deleted file mode 100644 index 1ee847b..0000000 Binary files a/images/create_repo.png and /dev/null differ diff --git a/images/git_dialog.png b/images/git_dialog.png deleted file mode 100644 index 3d132f8..0000000 Binary files a/images/git_dialog.png and /dev/null differ diff --git a/images/git_url.png b/images/git_url.png deleted file mode 100644 index e49172f..0000000 Binary files a/images/git_url.png and /dev/null differ diff --git a/images/prototype.png b/images/prototype.png deleted file mode 100644 index 0ae8d4e..0000000 Binary files a/images/prototype.png and /dev/null differ diff --git a/index.rst b/index.rst index 90c15f2..8e61340 100644 --- a/index.rst +++ b/index.rst @@ -19,20 +19,7 @@ piece of software is more complex. You are facing questions like: Below you find development tools and techniques that help you to write programs that get the job done and don’t fall apart. ----- -Getting Started ---------------- - -.. toctree:: - :maxdepth: 1 - - prototype.rst - version_control.md - folders.md - github_issues.md - --------------- Planning and Design ------------------- @@ -41,8 +28,8 @@ Planning and Design :maxdepth: 1 interface.md - class_diagram.md user_stories.md + github_issues.md crc_cards.md project_checklist.rst @@ -54,7 +41,6 @@ Packaging and Maintenance .. toctree:: :maxdepth: 1 - virtualenv.md pip.md pip_setup.md continuous_integration.md diff --git a/mandelbrot.py b/mandelbrot.py deleted file mode 100644 index 66eb597..0000000 --- a/mandelbrot.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Drawing the Mandelbrot set - -based on R code by Myles Harrison -http://www.everydayanalytics.ca - -original source of the Python code: - https://github.com/krother/Python3_Package_Examples - MIT License -""" -import numpy as np -from PIL import Image - -def get_next_iter(z, c, index): - """calculate the next generation of the entire matrix""" - newz = [] - for x in range(z.shape[0]): - for y in range(z.shape[1]): - if index[x, y]: - newz.append(z[x, y] ** 2 + c[x, y]) - else: - newz.append(999) - z = np.array(newz).reshape(z.shape) - return z - - -def calculate(z, k, c): - index = z < 2 - z = get_next_iter(z, c, index) - k[index] = k[index] + 1 - return z, k - - -def draw_mandelbrot(xmin=-2, xmax=1.0, nx=500, - ymin=-1.5, ymax=1.5, ny=500, - n=100): - x = np.linspace(xmin, xmax, nx) - real = np.outer(x, np.ones(ny)) - - y = np.linspace(ymin, ymax, ny) - imag = 1j * np.outer(np.ones(nx), y) - - c = real + imag - - z = np.zeros((nx, ny)) * 1j - k = np.zeros((nx, ny)) - - for recursion in range(1, n): - z, k = calculate(z, k, c) - - return k - - -if __name__ == '__main__': - mtx = draw_mandelbrot() - mtx = 255 * mtx / mtx.max() - mtx = mtx.astype(np.uint8) - im = Image.fromarray(mtx, 'L') - im.save('mandelbrot.png') diff --git a/profiling.md b/profiling.md deleted file mode 100644 index d4010fe..0000000 --- a/profiling.md +++ /dev/null @@ -1,23 +0,0 @@ - - -timeit, built-in Python module to measure the execution time of small code parts - -%time - -%timeit - -%%time - -switch from float64 to float32 see if it gets faster - -cProfile, the batteries included Python profiler - - -import cProfile -cProfile.run("[x for x in range(1500)]") - -python -m cProfile -s cumtime mandelbrot.py > profile.txt - -insert: - -z[index] = z[index] ** 2 + c[index] diff --git a/version_control.md b/version_control.md deleted file mode 100644 index cb3c404..0000000 --- a/version_control.md +++ /dev/null @@ -1,144 +0,0 @@ - -# Set up a Repository on GitHub - -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](https://www.github.com). - -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](https://realpython.com/python-git-github-intro/) 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](https://www.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: - -![create a repository](images/create_repo.png) - -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: - -![Git setup dialog](images/git_dialog.png) - -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](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). - -To clone the repository, you need the address under the big button labeled **Code**: - -![Copy the URL](images/git_url.png) - -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](https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/). - -In the end, you should see the code of your prototype on your GitHub page. -**Congratulations!** - -## Further Reading - -- [Git Introduction](https://realpython.com/python-git-github-intro/) -- [Try GitHub - Online-Tutorial](https://try.github.io/) -- [Pro Git](https://git-scm.com/book/en/v2) – the book by Scott Chacon diff --git a/virtualenv.md b/virtualenv.md deleted file mode 100644 index 080e89f..0000000 --- a/virtualenv.md +++ /dev/null @@ -1,62 +0,0 @@ - -# 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](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.