The More Strings Change the More They Stay the Same
The other thing you need to know about strings is that they are immutable; this means they can’t be
changed. In practice, it is possible to use some fairly simple functions to create a new string with an
edited value.
Creating a Text Application
It’s time to put all this information into practice on our role-playing game example. Strings are quite
simple to use; for the most part, you just need to make sure that you enclose your strings in matching
quotes properly. The top-level design for an RPG character-description generator script is
correspondingly simple.
# Prompt user for user-defined information
# Output the character sheet code-box
The minimum information I need is a name for each character. I’d also like to keep a note of each person’s gender and fantasy race, and I’d like some space for a short description, so I have created variables named Name, Desc, Gender, and Race to hold each of those values as a string. I can then print out those values in a pretty format using the print() function, as shown in Listing 3-1.
"""
chargen.py
Problem: Generate a description for a fantasy role-playing character.
Target Users: Me and my friends
Target System: GNU/Linux
Interface: Command-line
Functional Requirements: Print out the character sheet
User must be able to input the character's
name, description, gender and race
Testing: Simple run test
Maintainer: [email protected]
"""
__version__ = 0.1
Name = ""
Desc = ""
Gender = ""
Race = ""
# Prompt user for user-defined information
Name = input('What is your Name? ')
Desc = input('Describe yourself: ')
Gender = input('What Gender are you? (male / female / unsure): ')
Race = input('What fantasy Race are you? - (Pixie / Vulcan / Gelfling / Troll): ')
# Output the character sheet
fancy_line = "<~~==|#|==~~++**\@/**++~~==|#|==~~>"
print("\n", fancy_line)
print("\t", Name)
print("\t", Race, Gender)
print("\t", Desc)
print(fancy_line, "\n")code-box
Listing 3-1 is just a fancy version of hello_world, but the output looks a bit more exciting. You can run this from the command line in the same way; you will see all the values you enter displayed as a character sheet and formatted by the escape sequences.
$ python chargen.py code-box
The one new thing I’ve added is the line __version__ = 0.1 at the beginning. This is a magic variable: the name __version__ (with two underscores on either side) is a predefined variable with a special meaning to Python’s documentation tools. For now, I’m just going to use this to record the version number of this program; as I edit and refine the design, I will increment this number. Next, I need to generate some vital statistics to represent the different attributes that the characters will need to interact with the game world. This involves tackling the realm of numerical information.
# Output the character sheet code-box
The minimum information I need is a name for each character. I’d also like to keep a note of each person’s gender and fantasy race, and I’d like some space for a short description, so I have created variables named Name, Desc, Gender, and Race to hold each of those values as a string. I can then print out those values in a pretty format using the print() function, as shown in Listing 3-1.
chargen.py
Problem: Generate a description for a fantasy role-playing character.
Target Users: Me and my friends
Target System: GNU/Linux
Interface: Command-line
Functional Requirements: Print out the character sheet
User must be able to input the character's
name, description, gender and race
Testing: Simple run test
Maintainer: [email protected]
"""
__version__ = 0.1
Name = ""
Desc = ""
Gender = ""
Race = ""
# Prompt user for user-defined information
Name = input('What is your Name? ')
Desc = input('Describe yourself: ')
Gender = input('What Gender are you? (male / female / unsure): ')
Race = input('What fantasy Race are you? - (Pixie / Vulcan / Gelfling / Troll): ')
# Output the character sheet
fancy_line = "<~~==|#|==~~++**\@/**++~~==|#|==~~>"
print("\n", fancy_line)
print("\t", Name)
print("\t", Race, Gender)
print("\t", Desc)
print(fancy_line, "\n")code-box
Listing 3-1 is just a fancy version of hello_world, but the output looks a bit more exciting. You can run this from the command line in the same way; you will see all the values you enter displayed as a character sheet and formatted by the escape sequences.
The one new thing I’ve added is the line __version__ = 0.1 at the beginning. This is a magic variable: the name __version__ (with two underscores on either side) is a predefined variable with a special meaning to Python’s documentation tools. For now, I’m just going to use this to record the version number of this program; as I edit and refine the design, I will increment this number. Next, I need to generate some vital statistics to represent the different attributes that the characters will need to interact with the game world. This involves tackling the realm of numerical information.
Working with Numbers
First, assigning numbers to variables is fairly straightforward:
brainz = 4 code-box
As I mentioned earlier, if the interpreter encounters a bunch of characters starting with a numeral, rather than a letter or a quotation mark, it will assume that it is a number. This is why you can’t start variable names with a number. So far, so good. There are a few things that you need to know before you start trying to do math with computers.
Using Binary: Computers Can Only Count to One
All information is stored inside the computer as a series of ones and zeros, that is, in binary or base 2.
Your computer stores and processes data using a huge collection of tiny little switches, and these
switches can be either off (0) or on (1). This has led to the old programmers’ joke that “there are only 10
sorts of people in the world—those who understand binary and those who don’t” (10 in binary notation
represents two rather than ten).
Using Python, you don’t really need to know much more than this.
Bits and Bytes
You will frequently encounter the terms bits and bytes in computer literature. Bit is short for “binary digit”
and refers to one of these tiny switches that can only hold a “yes” or “no” answer. It is the smallest
possible unit of computer information. A byte is theoretically the amount of memory needed to store a
single character; this became standardized as 8 bits during the late twentieth century, even though
modern computers often use more than 8 bits to store characters.
alert-infoUsing Booleans: Truth or Dare
It follows from the preceding discussion that the simplest type of numerical value that exists on a
computer is one that has only two possible values: True (equal to 1) or False (equal to 0). These true/false values are known as Booleans, named after a system devised by English mathematician and
philosopher, George Boole. They can be manipulated using logical operators such as AND, OR, and NOT. I’ll
explain the meaning and use of these operators in Chapter 4. You can assign Boolean values by using the
True and False keywords:
illusion = Falsecode-box
Using Whole Numbers (Integers)
Whole numbers are known as integers in programming terms. Integers don’t have decimal points but
can be positive, negative, or zero. These are used to refer to numbers of things in much the way you’d
expect, like the eggs in the recipe example at the start of the chapter. It may be reassuring to know that
most of the time you can use numbers like these.
Performing Basic Mathematical Operations
You have learned how to store data inside variables, so let’s start manipulating that data. Integers can be
added, subtracted, and multiplied, the same as in the real world using the +, -, and * operators (the * is
the multiply sign). This creates an expression. An expression is a piece of code that has to be evaluated
(i.e., worked out) before you can discover its value. You can assign expressions to variables using statements like this:
>>> brainz = 7 - 3
>>> speed = 5 * -4
>>> strangeness = muscle + brainz * speed
>>> strangeness
-75code-box
All this works much as expected until the last line, where strangeness equals –75. How did that happen? Surely 5 + 4 = 9, multiplied by –20 would give –180? But no, what happens here is this:
How does Python decide that the arguments to the + are muscle and brainz*speed rather than just muscle and brainz? What happens here is that Python works out the arguments to * before it works out the arguments to +, and it picks as these arguments the smallest bits of the expression that make sense, in this case, brainz and speed. The multiplication operator * is said to bind tighter than +, the addition operator.
Understanding Operator Precedence
What you have just seen is an example of something much more general: given an expression to
evaluate, how does the Python interpreter decide which parts go with what, and how can you know what
its decision is going to be? The answer is that there is a predefined order called operator precedence,
which tells the interpreter which operators bind tightest. To see this order, you can look in the python
documentation at http://docs.python.org/3.0/reference/expressions.html#evaluation-order, or if
you’re unsure for a particular pair of operators, have a go in the interpreter with some values that will let
you distinguish the precedence: you can see from the output of 5 + 4 * –20 previously that * binds tighter
than +.
If you want the arguments to be grouped in a different way from Python’s default, you can get this to
happen using brackets, so in this example, you would type the following:
>>> charm
-180 code-box
Dividing Numbers
The division is performed using the / operator.
>>> print(13 / 5)
2.6
>>> print(13.75 / 4.25)
3.23529411765 code-box
If you want to do integer division, where the fractional part of the answer is dropped (also known as floor division), you can use the // operator instead of /. The remainder is obtainable by using the modulo % operator.
2.6
>>> print(13.75 / 4.25)
3.23529411765 code-box
If you want to do integer division, where the fractional part of the answer is dropped (also known as floor division), you can use the // operator instead of /. The remainder is obtainable by using the modulo % operator.
2
>>> print(13 % 5)
3 code-box
You may encounter results you don’t expect when doing integer division with negative numbers:
-3
>>> print(-13 % 5)
2 code-box
This is because floor division returns the largest whole number that is less than the result of the fractional division. So, –13/5 is –2.6 and the largest whole number less than –2.6 is –3. Now consider the remainder: the result of // multiplied by the divisor gives us the figure to work from to calculate the the remainder, as it would in all other remainder calculations:
Consider another example:
2
>>> print(-13 % -5)
-3 code-box
Here, 2 is the largest whole number less than 2.6, and –3 is calculated as follows:
Alternatively, you could avoid using this operator with negative numbers.
Working with Fractions and Floats
Fractional numbers are expressed using numbers before and after a decimal point using the float type.
Like integers, these numbers can be positive or negative. You don’t have to do anything particularly
special to assign a float to a variable; if you use a decimal point in the number, Python will assume that
it’s a float.
brainz = -13.678
speed = 0.0 code-box
Even if the part after the decimal point is zero, the number is considered to be a float. Floats can be manipulated using the same operators as integers, returning any fractional part as well.
Converting One Type into Another
Python has several convenient built-in functions that allow you to convert values from one type to
another. These are the most useful ones to start with:
- int(x) converts number x to an integer.
- float(x) converts number x to a float.
- str(object) converts more or less anything into a printable string.
23.0
>>> int(23.5)
23
>>> float(int(23.5))
23 code-box
There are a few gotchas when it comes to converting types; notice that float(int(x)) in the preceding example loses its fractional part. Not all conversions are reversible, and the result of the conversion may not be equal to the input value any more.
Working with Base 8 (Octal) and Base 16 (Hexadecimal)
It is possible to input and display integers in other formats such as base 16 (hexadecimal) or base 8 (octal).
Integers can be entered in octal form by putting a 0o in front of the octal number (that’s the number zero
followed by the letter o) and in hexadecimal by putting 0x (zero followed by x) in front of the hexadecimal
number. In hexadecimal, you use the letters A to F to represent the decimal numbers 10 to 15.
>>> hexadecimal_number = 0xFC6
>>> print(octal_number)
10
>>> print(hexadecimal_number)
4038 code-box
Let’s break things down to see what’s going on here. In a decimal number, the positions of the digits represent: units, tens, hundreds, thousands and so on—in reverse order. In hexadecimal and octal numbers, the positions represent exponents of the numerical base. In octal, these positions would represent units, 8s and 64s, for example, because 8 is the numerical base of octal.
You can work out the decimal value by multiplying the digits according to the value of their position
and adding the results together, as shown in Tables 3-2, 3-3, and 3-4. The first row in these tables is the
value of the position in the number, the second the multiplication sign to show that the value of the
position is multiplied by the digit in that position, the third the number in question, and the fourth the
results of the multiplication. Table 3-2 shows a decimal to give you the idea.
All three representations give 2037 as the resultant decimal value. Octals are encountered in
situations such as specifying file permissions and hexadecimals are used to specify colors, so it’s worth
familiarizing yourself with this notation if you have not encountered it before.
>>> gold = 0xFFCC00 code-box
The octal number assigned to permissions is an array of information where each bit has a distinct meaning: each octal digit sets read, write, and execute permissions for the user, group, and others respectively. The color value set for gold represents a mix of three values, red, green, and blue, which can range from 0x00 to 0xFF according to intensity.
It’s important to note that there is only one integer type here: what you get when you enter 0x10,
0o20, or 16 is exactly the same integer. If you want to get back the hexadecimal or octal string
representation of an integer, you can use the following functions.
- hex(x) displays integer x in hexadecimal format.
- oct(x) displays integer x in octal format.
>>> hex(x)
'0xdd'
>>> o = 0o77 + 0o33
>>> oct(o)
'0o132' code-box
Creating a Number Application
In order to take the character generation script any further, you will need to be able to compare values
and store the results. This will require knowledge of conditional statements (which I will move on to in
Chapter 4) and the use of more complex data types (see Chapter 5), so I will return to Cloud-Cuckoo
Land once you have had time to study these more advanced incantations. To demonstrate the use of
mathematical data types right now, let’s consider a simpler problem: calculating how much fabric you
would need to buy to make a pair of curtains.
To start, you need to define the problem again: calculate how much material to buy, given the size
of the windows. The functional requirements are that the user must be able to input the measurements
of the window and get back the required length of fabric in meters and the total price of the fabric to be
bought. To get a proper idea of how you might go about making a pair of curtains, you might want to talk
to real curtain makers, watch them in action, or at least get them to show you how they calculate the
amount of material they need. It might also be worth investigating your local fabric shop.
Following some research, my top-level design looked like this:
# Add a bit for the hems
# Work out how many widths of cloth will be needed
# and figure out the total length of material for each curtain (in cm still)
# Actually there are two curtains, so we must double the amount of material
# and then divide by 10 to get the number of meters
# Finally, work out how much it will cost
# And print out the result code-box
Before any calculation is possible, you will need to know how wide the roll of material is and how much it costs per meter. I have assumed width of 140 cm and a price of 5 units of currency per meter for this example. I can use the input() function to get the window_height and window_width from the user. The input() function returns a string, so I need to convert that into something Python recognizes as a number using float(). Once I have assigned the four starting values as suitable types, the calculation that follows is fairly straightforward as explained in the comments of Listing 3-2.
curtains.py
Problem: Calculate how much material to buy, given the size of the windows.
Target Users: My friend who wants to make some curtains
Target System: GNU/Linux
Interface: Command-line
Functional Requirements: Print out the required length of fabric in meters
Print out the total price of the fabric
User must be able to input the measurements of the window
Testing: Simple run test
Maintainer: [email protected]
"""
__version__ = 0.1
# To start with, all the measurements will be in cm
# Assume that the roll of material is going to be 140cm wide
# and that the price per meter will be 5 units of currency
roll_width = 140
price_per_metre = 5
# Prompt the user to input the window measurements in cm
window_height = input('Enter the height of the window (cm): ')
window_width = input('Enter the width of the window (cm): ')
# Add a bit for the hems
# First we must convert the string into a number
# otherwise we will get an error if we try to perform arithmetic on a text string
curtain_width = float(window_width) * 0.75 + 20
curtain_length = float(window_height) + 15
# Work out how many widths of cloth will be needed
# and figure out the total length of material for each curtain (in cm still)
widths = curtain_width / roll_width
total_length = curtain_length * widths
# Actually there are two curtains, so we must double the amount of material
# and then divide by 10 to get the number of meters
total_length = (total_length * 2) / 10
# Finally, work out how much it will cost
price = total_length * price_per_metre
# And print out the result
print("You need", total_length, "meters of cloth for ", price)code-box
Any of you who have actually set about making curtains will know that this is a gross oversimplification of the process. Those of you who can add numbers better than I can will realize that I have made a couple of dumb mathematical errors, so this script will return some crazy values, which won’t be of much use. Clearly, this is going to need some work, so let’s examine the problems.
Unless the width of each curtain is less than the roll_width, you will end up buying much more
fabric than you need. However, there is no way of working this out until you know what the initial
window measurements are. If the length of the curtains is less than the roll_width, you could turn the
whole thing on its side and just use one width of fabric (I’m assuming you’re using unpatterned fabric).
But if the curtains need to be both longer and wider than the roll_width, there is a problem: if the extra
material required is less than half the roll_width, you would need to buy an additional width of material
at the same length; if it is more than half, you would need to buy two additional widths, taking into
account the extra material needed for the joins. Still with me? Good. The script needs to take into account that fabric is sold by the meter (or half-meters) in whole widths, so I will need to round up the
widths to the nearest whole number and the final length to the nearest meter.
To deal with this using Python, it will be necessary to compare values and then execute different
calculations based on those conditions. This, incidentally, is what the next chapter is all about.
Jargon Busting
You encountered a lot of new terms in this chapter, so here are some more useful definitions:
- Binary (base 2): Binary arithmetic uses the digits 0 and 1. This corresponds to the electric current in a wire, which can only be on (value 1) or off (value 0)
- Bit: A bit is a digit in the binary number system. It can have two values, 0 or 1. The word is derived from binary digit.
- Boolean: Variables of this type can take only one of two values, True and False. These correspond to 1 and 0. This is the most appropriate return type for a function that uses its return value to report whether some condition holds or not.
- Built-in: Anything built-in is innate part of the programming language, as opposed to something that has to be imported from a module. A built-in element is part of Python’s standard library.
- Byte: A byte is the smallest unit of storage that can be accessed in a computer’s memory. It holds exactly 8 bits.
- Case-sensitive: In case-sensitive text, uppercase letters are treated as completely different characters from their lowercase counterparts. Treating uppercase and lowercase variants as the same character is known as case-insensitive.
- Characters: These are letters, digits, punctuation marks, and spaces—basically anything that can be typed in using a single key on the keyboard, even if it doesn't cause anything to be printed on the screen.
- Concatenate: When you create a string by joining together copies of two or more text strings without any spaces in between, you concatenate the string.
- Decimal (base 10): Decimal numbers are what you probably think of as normal numbers.
- Dynamic typing: Python determines the type and checks the correct usage of variables of different types during execution of a program rather than during compilation. Some other programming languages, like C, are statically typed: the compiler will not allow the use of a variable or function unless that function or variable has already been initialized and declared to be of a certain type. You don’t need to bother with declaring the type of your variables in Python.
- Expression: This refers to a section of code that can be worked out to produce a value.
- Flag: A flag is a Boolean variable used to record whether or not something has happened.
- Float: Float is short for “floating point” and is a fundamental type used to define numbers with fractional parts.
- Hexadecimal (base 16): Hexadecimal is base 16 arithmetic where each digit is a value from 0 to 15, rather than the 0 to 9 of base 10. The decimal numbers from 10 to 15 are represented by the letters A to F. Hexadecimal is a very convenient way of showing binary numbers, as every four binary digits can be shown as one hexadecimal digit.
- Integer: An integer is a fundamental (i.e., built-in) type used to define numeric variables holding whole numbers.
- Immutable: An immutable value cannot be modified.
- Logical operator: These commands perform basic manipulations on Boolean values.
- Mapping: In Python, a mapping is a data type that relates a set of keys to a set of values. It has nothing to do with planning your car journey.
- Octal (base 8): In octal arithmetic, each digit has a value of 0 to 7.
- Operator: These are commands, often represented by mathematical symbols, that perform simple manipulations on data, known as operands. Expressions take the following form: operand1 operation operand2.
- Operator precedence: This is the order in which operators are assigned their arguments when Python evaluates an expression. Where there is ambiguity, the operator with the higher precedence is assigned as arguments the smallest expressions on either side of it that make sense.
- Sequence: In Python, a sequence is an instance of a data type that consists of more than a single item. It does not refer to a series of statements to be executed one after another as in some other languages.
- Statement: This refers to a section of code that does something such as manipulate a piece of data, perform a calculation, or produce some output.
- String literal: This refers to words and sentences, and text composed of these, that is, any form of literal text.
- Truth testing: Evaluate whether a condition is True or False.
Summary
In this chapter, you have learned how to assign different types of values to variables, how to manipulate
simple text strings and perform basic mathematical operations using expressions and statements. Now,
you know how to construct and run a simple Python script that can perform a useful calculation. In the
next chapter, I will show you how to make decisions that can alter the way the program runs based on
those calculations.
Post a Comment