Python is one of the most widely used dynamic programming languages. It supports a rich set of packages, GUI libraries, and web frameworks that enable you to build efficient cross-platform applications. It is an ideal language for rapid application development. Such fast-paced development often comes with its own baggage that could bring down the overall quality, performance, and extensibility of the code. This book will show you ways to handle such situations and help you develop better Python applications. The key concepts will be explained with the help of command-line applications, which will be progressively improved in subsequent chapters.
This chapter will be an introductory one. It will serve as a refresher to Python programming. That being said, it is expected you have some knowledge of Python language, as well as object-oriented programming (OOP) concepts.
Here is how this chapter is organized:
We will start with installation prerequisites and set up a proper environment for Python development.
To set the tone right for the rest of the book, the next section will be a brief introduction to the high fantasy theme of the book.
What follows next is our first program. It is a simple text-based fantasy game, presented as a Python script.
We will add some complexity to this game and develop an incremental version of the game using simple functions.
Moving ahead, we will add more features to the game and redesign the code by applying OOP concepts.
The last topic will briefly cover Abstract Base Classes (ABCs) in Python.
The code explanation will be a bit verbose. More experienced readers can breeze past the examples and go to the next chapter, but be sure to understand the theme of the book and review the code in the ch01_ex03.py
file. In the next few chapters, you will learn techniques to progressively improve this code.
Before diving into the rest of the chapter, let's get some housekeeping out of the way. If you haven't already, you should read the Preface, which documents most of the following things:
Every chapter will have its own set of Python source files. Although we will talk through most of the code, you should keep the relevant files at hand.
The source code can be downloaded from the Packt Publishing website. Follow the instructions mentioned in the Preface.
The code illustrated in this book is compatible with Python version 3.5.1. The supporting code bundles also provide files compatible with version 2.7.9.
As noted before, it is assumed that you are familiar with basics of the Python language and know OOP concepts.
The book uses a fun, text-based game theme as a vehicle to explain various application development aspects. However, the book itself is not about developing game applications!
The solutions to the exercises (if any) are generally not provided.
The book provides several external links (URLs) for further reading. Over time, some of these links might end up being broken. If that ever happens, try searching the web with appropriate search terms.
Let's make sure that we have installed the prerequisites. Here is a table that summarizes the basic tools we need for this chapter and beyond; more verbose installation instructions follow in the next section:
Tool |
Notes |
---|---|
Python 3.5 |
The code illustrated in this book is compatible with version 3.5. See the next table for available Python distributions. Supporting code bundles also provide 2.7.9 compatible files. |
pip (package manager for Python) |
The pip is already available in the official distribution for versions 3.5 and 2.7.9. |
IPython |
Optional installation. IPython is an enhanced Python interpreter. |
Integrated development environment (IDE) |
Use the Python editor or any IDE of your choice. Some good IDEs are listed in a table later in this chapter. |
In subsequent chapters, we will need to install some additional dependencies. The Python package manager (pip) will makes this a trivial task.
Tip
Have you already set up the required Python environment or know how to do it? Just skip the setup instructions that follow and move on to the The theme of the book section, where the real action begins!
There are two options to install Python. You can either use the official Python version or one of the freely available bundled distributions.
For Linux or Mac users, Python is probably already installed on your system. If not, you can install it using the package manager of your operating system. Windows OS users can install Python 3.5 by downloading the Python installer from the official Python website:

During the installation process, just make sure to select the option that adds Python 3.5 to the system environment variable, PATH
, as shown in the preceding screenshot. You can also visit the official Python website, https://www.python.org/downloads, to get the platform-specific distribution.
Alternatively, there are several freely available Python distributions that bundle together useful Python packages, including pip and IPython. The following table summarizes some of the most popular Python distributions, including the official one:
Distribution |
Supported platforms |
Notes |
---|---|---|
| ||
Anaconda | ||
Windows, Linux, Mac | ||
Python(x, y) |
Windows |
Let's briefly talk about the path where Python is installed, and how to make sure python
is available as a command in your terminal window. Of course, things will widely vary, depending on where you install it and which Python distribution you choose.
Tip
The official Python documentation page has comprehensive information on setting up the Python environment on different platforms. Here is a link, in case you need further help beyond what we have covered: https://docs.python.org/3/using/index.html.
On a Unix-like operating system such as Linux, the default location is typically /usr/bin/python
or /usr/local/bin/python
.
If you used your operating system's package manager to install Python, the command python
or python3
should be available in the terminal window. If it isn't, you need to update the PATH
system environment variable to include the directory path to the Python executable. For example, if you have a
Bash shell, add the following to the .bashrc
file in your user home directory:
export PATH=$PATH:/usr/bin/
Specify the actual path to your Python installation in place of /usr/bin
.
On Windows OS, the default Python installation path is typically the following directory: C:\Users\name\AppData\Local\Programs\Python\Python35-32\python.exe
. Replace name
with your Windows username. Depending on your installer and system, the Python directory can also be Python35-64
. As mentioned earlier, at the time of installation, you should select the option Add Python 3.5 to PATH to make sure python
or python.exe
are automatically recognized as commands. Alternatively, you can rerun the installer with just this option checked.
Open a terminal window (or command prompt on Windows OS) and type the following command to verify the Python version. This command will work if Python is installed and is available as a command in the terminal window. Otherwise, specify the full path to the Python executable. For instance, on Linux you can specify it as /usr/bin/python
, if Python is installed in /usr/bin
:
$ python -V
Tip
Note that the $
sign in the previous command line belongs to the terminal window and is not part of the command itself! Put another way, the actual command is just python -V
. The $
or %
sign in the terminal window is a prompt for a normal user on Linux. For a root (admin) user, the sign is #
. Likewise, on Windows OS, the corresponding symbol is >
. You will type the actual command after this symbol.
The following is just a sample output, if we run the preceding command:
[user@hostname ~]$ python -V Python 3.5.1 :: Anaconda 4.0.0 (64-bit)
The pip is a software package manager that makes it trivial to install Python packages from the official third party software repository, PyPI. The pip is already installed for Python-2 version 2.7.9 or higher and Python-3 version 3.4 or higher. If you are using a different Python version, check out https://pip.pypa.io/en/stable/installing for the installation instructions.
On Linux OS, the default location for the pip is same as that of the Python executable. For example, if you have /usr/bin/python
, then pip should be available as /usr/bin/pip
. On Windows OS, the default pip.exe
is typically the following: C:\Users\name\AppData\Local\Programs\Python\Python35-32\Scripts\pip.exe
. As mentioned earlier, replace name
with your Windows username. Depending on your installer and the system, the Python directory can also be Python35-64
.
This is an optional installation. IPython is an enhanced version of the Python interpreter. If it is not already bundled in your Python distribution, you can install it with:
$ pip install ipython
After the installation, just type ipython
in the terminal to start the IPython interactive shell. Here is a screenshot of the IPython shell using the Anaconda Python 3.5 distribution:

Tip
It is often very convenient to use the Jupyter Notebook to write and share interactive programs. It is a web application that enables an interactive environment for writing Python code alongside rich text, images, plots, and so on. For further details, check out the project homepage at http://jupyter.org/. The Jupyter Notebook can be installed with:
$ pip install "ipython[notebook]"
Using an IDE for development is a matter of personal preference. Simply put, an IDE is a tool intended to accelerate application development. It enables developers to write efficient code quickly by integrating the most common tools they need. The Python installation comes with a program called IDLE. It is a basic IDE for Python, which should get you started. For advanced development, you can choose from a number of freely or commercially available tools. Any good Python IDE has the following minimum features:
A source code editor with code completion and syntax highlighting features
A code browser to browse through files, projects, functions, and classes
A debugger to interactively identify problems
A version control system integration such as Git
You can get started by trying out one of the freely available IDEs. Here is a partial list of popular IDEs. If you are just interested in a simple source code editor, you can check out https://wiki.python.org/moin/PythonEditors, for a list of available choices.
Have you read high fantasy novels, such as The Lord of the Rings or The Hobbit by J. R. R. Tolkien? Or watched the films based on these novels? Well, here is a high fantasy, "Tolkienesque" themed book on Python application development.
Tip
To find out more about J.R.R. Tolkien's work, see https://en.wikipedia.org/wiki/J._R._R._Tolkien. The term high fantasy is often used to represent a fantasy theme set in an alternate fictional world. Check out https://en.wikipedia.org/wiki/High_fantasy for more information.
This book takes you to an imaginary world where you will develop a text game based on the aforementioned theme. Yes, you can continue being a developer even in this imaginary world! During the course of the book, you will be accompanied by many fictional characters. While you learn different aspects of Python development, these characters will talk to you, ask questions, request new features, and even fight with the enemy.
It should be noted that this book is not about developing game applications. It uses a simple text-based game just as a medium to learn various development aspects.
Tip
Off topic, if you are interested in playing a high fantasy theme game, there are quite a few to choose from. Among the open source ones, Battle for Wesnoth is one of the most highly rated, free, turn-based strategy games with a high fantasy theme. Check out https://www.wesnoth.org, for more details.
Let's meet the imaginary characters who will accompany you in various chapters:
![]() |
Sir Foo A human knight who is portrayed as a grand knight guarding the southern plains. He is our main character and will be talking to us throughout the book. |
![]() |
Orc Rider An Orc is a human-like imaginary creature. Here, it is portrayed as an enemy soldier. The Orc is seen riding a wild boar-like creature. You will see this creature in this chapter. |
![]() |
Elf Rider An Elf is a supernatural mythical being. The Elf is mounted on an elvish horse. He is portrayed as a friendly. You will meet Mr. Elf in Chapter 6, Design Patterns. |
![]() |
Fairy An intelligent fairy with an inherent capability for magic. She will use her magic just once while finding her enchanted locket in Chapter 7, Performance Identifying Bottlenecks, (See O(log n)). You will first meet her in Chapter 6, Design Patterns. |
![]() |
Dwarf A Dwarf is a small human-like mythical being. He is portrayed as "The Great Dwarf" of the Foo mountains. He asks lots of questions. You will see him in the second half of the book, starting with Chapter 6, Design Patterns. |
With this fun theme as a vehicle, let's start our journey with a simple command-line application. It will be a text-based game. The complexities added in subsequent chapters will challenge you with interesting problems. The book will show you how to gracefully handle such situations.
We have the required tools and the environment set up. It is now time to write our first Python program. It will be a simple game of chance, developed as a command-line application. As we advance further, we will add more complexity to the game and learn new techniques to develop efficient applications. So, get ready for action!
The war between humans and their arch enemies, the Orcs, was in the offing. A huge army of Orcs was heading toward the human establishments. They were virtually destroying everything in their way. The great kings of the human race joined hands to defeat their worst enemy for the great battle of their time. Men were summoned to join the rest of the army. Sir Foo, one of the brave knights guarding the southern plains, began a long journey toward the east, through an unknown dense forest. For two days and two nights, he moved cautiously through the thick woods. On his way, he spotted a small isolated settlement. Tired and hoping to replenish his food stock, he decided to take a detour. As he approached the village, he saw five huts. There was no one to be seen around. Hesitantly, he decided to enter a hut... |

You are designing a simple game in which the player is required to choose a hut for Sir Foo. The huts are randomly occupied either by a friend or an enemy. It is also possible that some huts remain unoccupied. If the chosen one turns out to be an enemy hut, the player loses. In the other two scenarios, the player wins.
Now that the goal is clear, open your favorite editor and note down the main steps. This is sometimes referred to as a pseudo code.
While the user wishes to keep playing the game:
Print the game mission
Create a
huts
listRandomly place
'enemy'
or'friend'
or'unoccupied'
in 5 hutsPrompt the player to select a hut number
if enemy
: print"you lose"
else
: print"you win"
As you will notice, the key piece of the code is to randomly occupy the five huts with either enemy or friend and keep the remaining ones unoccupied. How do we do this? Let's quickly work this out using the Python interpreter. If you have installed IPython, start the IPython interpreter. Otherwise, just use the default Python interpreter by typing the command python
in a terminal window. First, we need a Python list to hold all the occupant types. Next, we will use the built-in random
module and call random.choice
to pick one element randomly from this list. This code is shown in the following screen capture:

Now, we just need to write the surrounding code. Let's review it next.
Download the source code, ch01_ex01.py
, from the supplementary code bundle provided for this chapter. The file extension, .py
, indicates that it is a Python file. Open it in a Python editor or an IDE of your choice. It is recommended that you keep this file handy while reading the following discussion. It is often easier to glance at the full code to understand it better. Observe the following code snippet. It is just a small portion of the code inside the if __name__ == '__main__'
condition block in the aforementioned file.
Tip
If you have Python 2.7.9 installed, there is a separate Python 2.7.9 compatible source provided in the supporting code bundle.

Let's review the code snippet in the preceding screenshot:
The first two lines import two built-in modules to gain access to the functionality provided within these modules. The
textwrap
module essentially provides features to nicely format the messages printed on the command line.The
if
condition block,if __name__ == '__main__'
, is invoked only when the file is run as a standalone script. In other words, the code inside this condition block won't be executed if you import this file in some other file.Now, let's look at the code in this condition block. First, we will initialize a few variables. As demonstrated earlier, the list
occupants
stores the potential occupant types for the hut.The last few lines are just to format the text printed in the terminal window. The
dotted_line
is a string that will show a 72-character long line with hyphen symbols.The ASCII escape sequence is used to print the text in bold. The sequence
"\033[1m"
is to make bold text, and"\033[0m"
is to go back to normal printing style.
The next few lines essentially print further information about the game in the console:

Let's have a look at the code from the preceding screenshot:
The variable
msg
is a very long string. This is where thetextwrap
module is used.The
textwrap.fill
function wraps the message in such a way that each line is 72 characters long, as specified by thewidth
in our code.
Now, let's review the following while
loop.
Tip
For Python 2.7.9, the only change required in the first example is to replace all the calls to the built-in function input
with raw_input
:
# For Python 2.7 user_choice = raw_input(msg)

This top-level loop gives the player an option to play the game again.
Using
random.choice
, we randomly pick an occupant from the list ofoccupants
and add it to thehuts
list. This was illustrated earlier.The built-in
input
function accepts a hut number of the user's choice as an integer. Theidx
variable stores a number.
Next, it reveals the occupants by printing related information. Finally, it determines the winner by checking the list item corresponding to the hut number. Note that the huts
list index starts at 0. Therefore, to retrieve the list element for a given hut number, idx
, we need to check the list index at idx-1
.
Assuming you already have Python in your system environment variable, PATH
(available as either python
or python3
), run the program from the command line as:
$ python ch01_ex01.py
That's all! Just play the game and try to save Sir Foo by choosing the right hut! The following snapshot of a Linux terminal window shows our game in action:

In the last section, you wrote a quick set of instructions to create a nice little command-line game. You asked your friends to try it out and they kind of liked it (perhaps they were just trying to be nice!). You received the first feature request for the game.
"I think this game has good potential to grow. How about including combat in the next version of the game? When Sir Foo encounters an enemy, he should not just give up that easily. Fight with the enemy! Let the combat decide the winner. "-your friend |

You liked the idea and decided to add this capability to the code in the next version. Additionally, you also want to make it more interactive.
The script you wrote for the first program was small. However, as we go on adding new features, it will soon become a maintenance headache. As a step further, we will wrap the existing code into small functions so that the code is easier to manage. In functional programming, the focus is typically on function arrangement and their composition. For example, you can build complicated logic using a simple set of reusable functions.
Before adding any new features, let's revisit the script that you wrote in the previous version (version 0.0.1). We will identify the blocks of code that can be wrapped into functions. Such code chunks are marked in the two code snippets that follow:

We will wrap most of the highlighted code into individual functions, as follows:
1: show_theme_message 2: show_game_mission 3: occupy_huts 4: process_user_choice 5: reveal_occupants 6: enter_hut

In addition to these six blocks of code, we can also create a few top-level functions to handle all this logic. In Python, the function is created using the def
keyword, followed by the function name and arguments in parentheses. For example, the reveal_occupants
function requires the information about the huts
list. We also need to optionally pass the dotted_line
string if we do not want to recreate it in the function. So, we will pass the hut number idx
, the huts
list, and the dotted_line
string as function arguments. This function can be written as follows:

After this initial work, the original script can be rewritten as:

This is much easier to read now. What we just did is also referred to as refactoring; more on various refactoring techniques in a later chapter. It makes it easier to do changes to the individual methods. For example, if you want to customize the mission statement or scenario description, you do not need to open the main function, run_application
. Similarly, occupy_huts
can be expanded further without any clutter in the main code.
In the previous section, we wrapped the game logic into individual functions. This not only improved the code readability, but also made it easier to maintain. Let's move on and include the new attack()
function in the game. The following steps show the logic of the game with the attack feature included.
While the user wishes to keep playing the game:
Print game mission
Create a
huts
listRandomly place
'enemy'
,'friend',
or'unoccupied'
in 5 hutsPrompt the player to select a hut number
if
the hut has an enemy, do the following:while
the user wishes to continue the attack, use theattack()
method on the enemyAfter each attack, update and show the health of Sir Foo, and of the enemy too;
if enemy health <= 0:
print"You Win".
But,
if Sir Foo health <= 0:
print"You Lose".
else
(hut has a friend or is unoccupied) print"you win"
Initially, Sir Foo and the Orc will have full health. To quantify health, let's assign hit points to each of these characters (or the game units). So, when we say the character has full health, it means it has the maximum possible hit points. Depending on the character, the default number of hit points will vary. The following image shows Sir Foo and the Orc with the default number of hit points, indicated by the Health label:

The bar above the Health label in the image represents a health meter. Essentially, it keeps track of the hit points. In the discussion that follows, we will use the terms hit points and health meter interchangeably. During the combat, either the player or the enemy will get injured. For now, neglect the third possibility where both escape unhurt. An injury will reduce the number of available hit points for the injured unit. In the game, we will assume that in a single attack turn only one of the characters is hit. The following image will help you imagine one such attack turn:

Here, Sir Foo's health meter is shown as the maximum and the Orc has sustained injuries!

Hmm, the Orc thinks he can defeat Sir Foo! This is interesting. Let's develop the game first and then see who has a better chance of winning! |
With this understanding of the problem, let's review the code that implements this feature.
Download the source file, ch01_ex02.py
, from the chapter's code bundle and skim through the code. The key logic will be in the attack()
function. We will also need a data structure to keep the health record of Sir Foo and the enemy. Let's start by introducing the following utility functions that take care of some print business:

Now, look at the main function, run_application
, and the supporting function, reset_health_meter
. In addition to introducing the dictionary health_meter
, we have also encapsulated the game logic in play_game
:

At the start of a new game, the values of the health_meter
dictionary are set back to the initial ones by calling reset_health_meter
:

Next, let's review the play_game
function. If the hut has the enemy, the player will be asked if the attack should be continued (the start of the while
loop). Based on the user input, the code calls the attack
function or exits the current game:

The enemy is attacked repetitively using the interactive while
loop, which accepts user input. Execution of the attack
function may result in injury to Sir Foo, or the enemy, or both. It is also possible that no one gets hurt. For simplicity, we will only consider two possibilities: a single attack that will injure either the enemy or Sir Foo. In the previous section, we used the built-in random number generator to randomly determine the occupants of the huts. We can use the same technique to determine who gets hurt:
injured_unit = random.choice(['player', 'enemy'])
But hold on a minute. Sir Foo has something to say: |

We should take into account the chance of an injury to the player and to the enemy. In the attack
function shown next, we will assume that for about 60% of the time, the enemy will get hit and for the remaining 40%, it is Sir Foo who is on the receiving end.
The simplest way is to create a list with 10 elements. This list should have six entries of 'enemy'
and four entries of 'player'
. Then, let random.choice
select an element from this list. You can always introduce a difficulty level in the game and change this distribution:

Once the injured_unit
is selected randomly, the injury
is determined by picking a random number between 10
and 15
, inclusive. Here, we use the random.randint
function. The final important step is to update the health_meter
dictionary for the injured unit by reducing its number of hit points.
The attack feature that you added in the previous game has made it a lot more interesting. You can see some friends coming back again and again to play the game. The new feature requests have started pouring in.
Here is a partial list of the requested features:
New mission to acquire all the huts and defeat all the enemies. This also means the hut occupants should be revealed right at the beginning of the game.
Ability to get healed in a friendly or unoccupied hut.
Ability to abandon combat (or run away from the enemy). This is a strategic move to run away, get healed in a friendly hut, and resume combat.
Introduce one or more horse riders to assist Sir Foo. They can take turns to acquire huts. Ideally, a user-configurable option.
Ability to configure the maximum hit points for each enemy unit and each of the horse riders.
Configurable total number of huts; for example, increase it to 10.
Each hut can have either some gold or a weapon inside that Sir Foo and his friends can pick up.
Have an elf rider join Sir Foo. His abilities give him a very high chance of winning with fewer attacks.
This is quite a long list. You are preparing a plan. Here is a partial list of things you will need to add to the existing code to implement some of these features:
Keeping track of the hit points of multiple enemy units occupying various huts
Maintaining the health record of Sir Foo and all accompanying horse riders
Monitoring how many huts are acquired by Sir Foo's army
Another dictionary or list to keep track of the gold in each hut, and another one for weapons; additionally, what if someone wants to put armor in the hut?
Not to forget, yet another list of dictionary for each unit that accepts any of these goodies
Ah! So they want an elf rider with its own traits and abilities...nice...thanks for the additional trouble!
That is already a long list. While you could still continue to use the functional programming approach, in such scenarios it will get tougher as the game evolves and new features get added.
Thankfully, object-oriented programming comes to the rescue. How about making Sir Foo an instance of a Knight
class
? With this, it should be easy to manage parameters relevant to Sir Foo. For example, an attribute, hitpoints
, can be used to keep track of Sir Foo's health instead of using the health_meter
dictionary in the earlier example. Similarly, the other attributes in the class can keep track of the amount of gold or weapons collected while acquiring the huts (another requested feature).
There is a lot more beyond this bookkeeping. The various methods of the class would enable a specific implementation of behaviors, such as attack, run, heal, and so on. The horse riders accompanying Sir Foo can also be instances of the class Knight
. Alternatively, you can create a new class called HorseRider
for all these units that accept commands from Sir Foo.
For this new version, let's hand pick a few requested features from the earlier list. In fact, Sir Foo should be the one who makes this call:

As you wish, Sir Foo...we will only add the new heal feature in this version. |
It is now time to clearly define the targets for this release. You are not just adding new features to your application, but also making some fundamental changes to the code to accommodate future requests.
In this version, the mission is to acquire all of the five huts. Here, you will implement a new heal
feature to regain all the hit points for Sir Foo. You will also implement some strategic controls, such as running away from combat, getting healed in a friendly hut, and then returning rejuvenated to defeat the enemy.
We already discussed how creating a Knight
class will help simplify the handling of data and all other things related to Sir Foo, be it the hit points or the way he attacks enemies.
What other classes can be carved out? How about having the enemy as an object? The enemy could occupy multiple huts. Remember that we need to defeat all the enemies. Imagine the following scenario: Sir Foo injures an enemy in hut number 2, thereby reducing its hit points. Then, he moves on to another hut occupied by another enemy. Now, we need to maintain two separate hit point counters for each of these enemy units.
In a future version, you can expect users to ask for different enemy types with the ability to attack or heal, just like how we have it for Sir Foo. So, at this point, it makes sense to have a separate class, instances of which represent the enemy units. We will name this class OrcRider
. It will have similar attributes to the Knight
class. However, for simplicity, we will not give the enemy capabilities such as healing, changing huts, and so on.
Sir Foo says he is delighted to read that the enemy has been denied some important capabilities. (But you can't see his happy face behind the helm.) |
There is something else we should consider. So far, huts
was just a simple Python list
object holding information about the occupant types as strings.
Looking at the requested features list, we also need bookkeeping for the amount of gold and armor in the hut and to update its occupant, depending on the result of the fight. In a future version, you may also want to show some statistics, such as a historic record of the occupants, changes in the amount of gold, and so on. For all this and more, we will create a class, Hut
.
Take a pen and paper and write down the important attributes we need for each class discussed so far. At this point, do not worry about classifying whether it is an instance variable or a class method that encapsulates instructions to perform specific tasks. Just write down what you think belongs to each class.
The following schematic shows a list of potential attributes for the Knight
, Hut
, and OrcRider
classes. The attribute names in strikethrough text indicate the potential attributes that won't be implemented in this illustration. But, it is always good to think ahead and keep it at the back of your mind during the design phase of the application:

This is not a complete specification, but we have a good starting point now. When Sir Foo enters an enemy hut, we have a choice to call the attack
method of the Knight
class. As before, the attack
method will randomly pick who gets injured and deduct the hit points for that character. In the Knight
class, it is convenient to have a new attribute, enemy,
that will represent the active opponent. In this example, enemy
will be an instance of the OrcRider
class.
Let's develop this design further. Did you notice that the Knight
and OrcRider
classes have several things in common? We will use the inheritance principle to create a superclass for these classes, and call it GameUnit
. We will move the common code to the superclass, and let the subclasses override the things they want to implement differently. In the next section, we will represent these classes with a Unified Modeling Language (UML)-like diagram.
The following diagram will help develop a basic understanding of how the various components talk to each other:

The preceding diagram is similar to a UML representation. It helps create a visual representation of a software design. In this book, we will loosely follow the UML representations. Let's call the diagrams used here pseudo UML diagrams (or UML-like diagrams).
An explanation is in order for the UML-like convention used here. We will represent each class in the schematics as a rounded rectangle. It shows the class name followed by its attributes. The plus sign (+) before the attribute indicates that it is public. A protected or private method is generally represented with a negative sign (-). All the attributes shown in this diagram are public attributes. So, optionally, you could add a plus sign next to each attribute. In later chapters, we will follow this convention. For ease of illustration, only a few relevant public attributes will be listed. Observe that we are using different types of connectors in this diagram:
The arrowhead with an empty triangle symbol represents inheritance; for example, the
Knight
class inherits from theGameUnit
classThe arrowhead with a filled diamond symbol represents object composition, for example, a
Hut
instance has an object of theGameUnit
class (or its subclasses)The arrowhead with an empty diamond symbol represents object aggregation
Now, let's talk about the individual components of the diagram presented earlier.
The Knight
and OrcRider
classes inherit from GameUnit
. The Knight
class, in this case, will override default methods, such as attack
, heal
, and run_away
. The OrcRider
class will not have such overridden methods, as we will not give these capabilities to the enemy.
The Hut
class will have an occupant. The occupant can either be an instance of the Knight
or the OrcRider
, or the None
type if the hut is unoccupied. The filled diamond connector in the diagram indicates composition.
Tip
Object composition
It is an important OOP principle. It implies a has-a relationship. In this case, Hut
contains, or is composed of, some other object that is to be used to perform specific tasks. Just say it out loud; a Hut
has-a Knight
, a Hut
has-an OrcRider
, and so on.
In addition to the four classes discussed, we will introduce another one to encapsulate the top-level code. Let's call it AttackOfTheOrcs
. As there are five huts, a class method in AttackOfTheOrcs
creates that number of Hut
instances. This is object aggregation, shown by the empty diamond shaped arrow in the preceding diagram.
Have you noticed another has-a relationship in AttackOfTheOrcs
? The player
attribute in this class is an instance of the Knight
class, but in the future, this could change. This relationship is indicated by the filled diamond-head connector joining the Knight
and AttackOfTheOrcs
boxes.
With this high-level understanding, let's begin developing the code. Download the Python source file, ch01_ex03.py
. We will review only a few important methods in the code. Refer to this source file for the complete code.
Tip
The code for this example, ch01_ex03.py
, is all squished inside a single file. Is it good practice? Certainly not! As we go along, you will learn about best practices. Later in the book, we will discuss some important building blocks of application development, namely refactoring, coding standards, and design patterns. As an exercise, try to split the code into smaller modules and add code documentation.
The main execution code is shown here, along with some details of the AttackOfTheOrcs
class. In the __init__
method, we will initialize some instance variables and later update the values they hold. For example, self.player
represents the instance of the Knight
class when the game begins:

Tip
Just as a refresher, the __init__
method is somewhat similar to a constructor in languages such as C++; however, keep in mind some differences. For example, you cannot overload __init__
as you might do in these languages. Instead, you can easily accomplish this using optional arguments or the classmethod
decorator. We will cover some aspects later in the book.
Let's quickly review the play
and _occupy_huts
methods:

The self.player
is an instance of the Knight
class. We will call the acquire_hut
method of this instance where most of the high-level action happens. After this, the program simply looks for the health parameters of the player and the enemy. It also queries the Hut
instance to see if it is acquired.
Moving ahead, in the _occupy_hut
method, the objects of Hut
are created and appended to the self.huts
list. This method is shown in the following figure:

Note
Public, protected, and private in Python
You will notice that some methods of the AttackOfTheOrcs
class start with an underscore, for example, _process_user_choice()
. That is a way to say that this method is not meant for public use. It is intended to be used from within the class. Languages such as C++ define class access specifiers, namely, private
, protected
, and public
. These are used to put restrictions on the access of class attributes.
There is no such thing in Python. It allows outside access to the attributes with a single underscore as game._process_user_choice()
. If the attribute name starts with double underscores, you can't call it directly. For example, you can't directly call game.__process_user_choice()
. That being said, there is another way to access such attributes from outside. But let's not talk about it. Although Python allows you to access such attributes, it is is not good practice to do so!
Observe the acquire_hut
method of the Knight class:

Let's talk through this method next:
First, we need to check whether the hut's occupant is a friend or an enemy. This is determined by the variable
is_enemy
, as shown in the preceding figure.The hut's occupant can be of the following types: an instance of the
Knight
class, an instance of theOrcRider
class, or set toNone
.The
GameUnit
class, and its subclassesKnight
andOrcRider
, define aunit_type
attribute. This is just a string that is set as either'friend'
or'enemy'
.Thus, to determine whether there is an enemy hiding in the hut, we will first check whether the
hut.occupant
is an instance of the superclassGameUnit
. If true, we will know it has aunit_type
parameter. So, we will check whetherhut.occupant.unit_type
is equal to'enemy'
. For theOrcRider
class,unit_type
is set to'enemy'
by default.The rest of the logic is simple. If the occupant is an enemy, it asks the user what to do next: attack or run away.
The
Knight.attack
method is similar to the one discussed earlier. One change here is that we can access thehealth_meter
attribute of the injured unit and update it.If
hut.occupant
happens to be'friend'
orNone
, it callshut.acquire()
.
What happens when the Hut.acquire()
method is called? Here is the code snippet for the Hut
class:

The acquire
method simply updates the occupant
attribute with the object passed as an argument to this method.
It's play time! We have reviewed the most important methods of the new classes. You can review the rest of the code from the ch01_ex03.py
file, or better try to write these methods on your own. Run the application from the command line, like we did earlier. The following screenshot shows the game in action:

In the previous section, we redesigned the code using the OOP approach. We also demonstrated the use of inheritance by defining a superclass GameUnit
, and inheriting from it to create the Knight
and OrcRider
subclasses. As the last topic in this chapter, let's talk about using abstract base classes in Python.
Tip
This section is intended to provide a basic understanding of ABCs in Python. The discussion here is far from being comprehensive but will be just enough to implement an ABC in our application code. For further reading, check out the Python documentation at https://docs.python.org/3/library/abc.html.
If you are familiar with OOP languages such as Java or C++, you probably already know the concept of an ABC.
A base class is a parent class from which other classes can be derived. Similarly, you can have an abstract base class and create other classes that inherit this class. So, where is the difference? One of the major differences is that an ABC can't be instantiated. But that is not the only difference. An ABC forces the derived classes to implement specific methods defined within that class. This much knowledge about an ABC should be good enough to work through the examples in this book. For more details, see the aforementioned Python documentation link. Let's review a simple example that shows how to implement an abstract base class in Python and how it differs from an ordinary base class. The abc
module provides the necessary infrastructure. The following code snippet compares the implementation of an ABC to an ordinary base class:

The class on the left, AbstractGameUnit
,
is the abstract base class, whereas the GameUnit
class on the right is an ordinary base class. The three differences in the ABC implementation are marked with numbers, as shown in the preceding screenshot.
The argument
metaclass=ABCMeta
is used to defineAbstractGameUnit
as an ABC.The
ABCMeta
is a metaclass to define the abstract base class. It is a broad discussion topic, but the simplified meaning of a metaclass is as follows: to create an object, we use a class. Likewise, imagine a metaclass as one used to create a class.A Python decorator provides a simple way to dynamically alter the functionality of a method, a class, or a function. This is a special Python syntax that starts with an @ symbol followed by the decorator name. A decorator is placed directly above the method definition.
The
@abstractmethod
is a decorator that makes the method defined on the next line an abstract method.The abstract method is the one that the ABC requires all the subclasses to implement. In this case,
AbstractGameUnit
requires itsKnight
subclass to implement theinfo()
method. If the subclass does not implement this method, Python simply doesn't instantiate that subclass and will throwTypeError
. You can try this by removing theKnight.info
method and running the code.There is no such restriction if the
Knight
class inherits from an ordinary base class, such asGameUnit
.
Tip
The code illustrated here is for Python version 3.5. For version 2.7, the syntax is different. Refer to the ch01_ex03_AbstractBaseClass.py
file in the Python2 directory of the supporting material for an equivalent example.
In the ch01_ex03.py
file, you will see some comments. These are intentionally kept to give you an opportunity to improve portions of the code. There is plenty of room for improvement in this code. See if you can rewrite portions of the code to make it more robust. If you prefer a well-defined problem, here is one:
The Knight
and OrcRider
classes inherit from the GameUnit
superclass. This exercise is about converting GameUnit
to AbstractGameUnit
, an abstract base class. Here is a cheat sheet for you; the skeleton code shown in the following figure is with the Python 3.5 syntax.
Refer to the ch01_ex03_AbstractBaseClass.py
file:

In this chapter, we touched upon some introductory concepts in Python to develop a simple command-line application. We first equipped ourselves by setting up a Python development environment.
The first program we wrote was a simple Python script. We soon realized that a simple script would be hard to maintain if more features are added. As a next step, we did a bit of refactoring and wrapped the code inside functions. This improved the code readability and also made it easier to manage. The proposed introduction of more features to the application made us rethink the design. We learned how to transform the code into an object-oriented design and implemented a few of these new features.
And how can we forget Sir Foo! He will accompany us throughout this book.
Is the code developed free from bugs? You might have already noticed some problems while playing the game! In the next chapter, we will see how to make the application more robust by handling exceptions.
The code illustrations that you see in this book are actually image files or code snapshots.
The rendering quality of these images will vary depending on your PDF reader's page display resolution and the zoom level.
If you have trouble clearly reading this code, you may try the following in your PDF or e-book reader:
Set the zoom level to 100%
Use the page display resolution of 96 pixels/inch or similar
If the problem still persists, you can try with a different resolution.
How do you set this resolution? It will depend on your e-book reader. For example, if you are using Adobe Reader, go to Edit | Preferences and then select Page Display from the left panel. You will see Resolution as an option in the right panel. Select 96 pixels/inch or similar and see if that helps render the images better.