Chapter 5: Strings

We started off by treating the Python interpreter as a calculator, and we took a good look at how numbers are defined and manipulated in the language. Now we'll move on to some more complicated values by investigating how Python deals with strings. A string is a collection of characters, such as a word, a sentence, or a proper name. It might be a date, or it might even be a number. The crucial difference is that the data is treated as a piece of text. In this chapter, you'll see how strings are defined and used in Python, and you'll discover the new ways you can use these data values.

Text, the Python way

A string is Python's data type for storing a piece of text.

>>> "Hello World"
'Hello World'

Strings can be enclosed in single quotes, double quotes, or even triple quotes, depending on the way you like to input your variables. They all have slightly different formatting, and it is probably best to treat each case by example. If you've stored some characters in between quotes, you've got a variable that Python understands to be a string type.

>>> 'Alexander'
'Alexander'
>>> 'Python isn\'t too tricky after you\'ve had some practice.'
"Python isn't too tricky after you've had some practice."
>>> '"Python is great," he said.'
'"Python is great," he said.'

Single quotes are a straightforward way to input strings. In the example above, the third statement shows the use of double quotes.

Something new has happened in the second example, where we actually attempt to use a single quote in the string. To tell Python to actually use an apostrophe in the string value instead of indicating that the string is complete, we have to place a backslash before the single quote. This might seem odd at first, so let's see what would happen if we just tried to add the single quote in the string without any sort of special notification to the Python interpreter.

>>> 'Python isn't too tricky after you've had some practice.'
SyntaxError: invalid syntax

Once Python gets to the word "isn't" and hits the single quote character, the interpreter tries to match the new quote to the first single quote at the start of the string. In other words, it believes you've written the string 'Python isn', and accidentally left some other characters at the end. Without the backslash, Python has no idea that we expect the string to continue, and just closes off the variable right there. If you see a syntax error like this, a common solution is to go back to the nearby strings and to verify that your quotes are all correct. If you'd like to use a single quote in your string that is itself single quoted, remember to place a backslash before the quotes you'd like to retain in your string!

Double quotes are very similar to single quoted strings, so the examples will look very familiar.

>>> "Alexander"
'Alexander'
>>> "Python isn't too tricky after you've had some practice."
"Python isn't too tricky after you've had some practice."
>>> "\"Python is great,\" he said."
'"Python is great," he said.'

The resulting values that show up on the screen are exactly the same as in the first set of single quoted string examples. The only changes that were made were swapping out single and double quotes, using backslashes in the appropriate places, and omitting the unnecessary backslashes. This is part of the reason that this book recommends starting with double-quoted strings; you won't find much reason as a new programmer to swap between the two, and if you do end up using a mix of single-quoted and double-quoted strings, you might mix things up somewhere along the line and run into syntax error issues. (I speak from experience!)

Triple-quotes are a rare beast, and there really won't be too many instances where you'll need them. A triple-quoted string treats anything inside of it, including new lines where a statement would normally end, as part of the actual string. They're good for extremely long strings with lots of specific formatting.

>>> print('''This is a long string.
    Python is letting me make a very long string.
        There are some spaces before this sentence.
    What a long string!''')
This is a long string.
    Python is letting me make a very long string.
        There are some spaces before this sentence.
    What a long string!

Strings are immutable

Strings are a special kind of data type! Strings are an immutable type, meaning that they can't be changed once they're created. Rather than applying changes to our strings, like changing the second letter to an "e", or capitalizing the first letter, we create new strings based on the old one. It's different than saying that once you declare a variable and set it to a string, you can't set that variable to a different one. It's more like saying that if my name is Alexander, and I change my name to Sam, it isn't the case that everyone else in the world named Alexander will also now be known as Sam. I reference a new value when I change my name, I don't change the value itself. Sure, this example might sound silly, but let's try to motivate it with a number example.

Python numbers are also immutable. There is no way to change the number 1 to the number 2. A number is a number, and that's that. We can change a variable that stores the value 1 to reference the value 2, but that doesn't change the actual conceptual value 1 to the other value 2. The same idea applies to strings. If I changed my name to Sam, it wouldn't mean that every Alexander around the world would do the same.

Why even bother to mention this? Again, it might sound silly to even make a point of discussing it. The problem is that we're dealing in terms of variables on a computer, and we can easily imagine cases where we'd want to modify a string. For example, say we wanted to get a string from the user corresponding to their name. If they didn't capitalize it, we'd do it for them.

>>> name = 'alexander'
>>> name.capitalize()
'Alexander'

This looks different than our previous examples, where we'd expect to see a function call like capitalize(name). Since a string is an immutable variable, we're not expecting to capitalize the string in-place, just like we're not expecting to round the actual number 2.4, forever wiping out 2.4 and replacing it with the actual number 2. We want to make sure that name gets the result of the capitalize function. We want to get a new string that is capitalized, and we'd like name to refer to that new string.

>>> name = 'alexander'
>>> name
'alexander'
>>> name.capitalize()
'Alexander'
>>> name
'alexander'
>>> name = name.capitalize()
>>> name
'Alexander'

If you create a string with your name stored in it, unless you change that variable later to point to another string, that variable is always going to be your name. Nobody can change it unless you modify the reference yourself.

Let's use a few more examples to show what this actually means.

One of the built-in string features is the ability to look at individual letters in a string. We can use the square-brackets on the keyboard to reference individual positions in a string. By placing square brackets after a string with a number inside, we ask for the letter at the index referred to by that number. For example, if we have a variable, we can look at each of the letters, one after the other.

>>> x = "Alexander"
>>> x[0]
'A'
>>> x[1]
'l'
>>> x[2]
'e'
>>> x[3]
'x'
>>> x[4]
'a'

So when we say x[0], we ask for the letter at position 0 in the string, which in this case is the letter "A". It's pretty nice to be able to reference each letter in a string like this, but we have to answer one thing right off the bat. First question, why on Earth is the first position zero? Why does x[0] point to the first letter in the string?

It's correct to think about string positions as being indexed by the distance from the start of the string. The first position in the string, which in this case is the capital letter "A", is 0 letters away from the beginning of the string. Sounds a bit funny, right? For that reason, we say that x[0] is the first letter in the string x. The second letter "l" is one letter away from the start of the string, and so x[1] is "l". However, since our string is nine letters long, what does this mean for x[9]?

>>> x[9]
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
        x[9]
IndexError: string index out of range

Oops. What happened? Python is telling us that we've asked for a letter index outside the number of characters in the actual string. If we reference a position outside the range of the string, we hit one of these IndexError warnings. These letters don't exist, so Python throws an IndexError at us to let us know we've gone too far. To exaggerate the point, take a look at this.

>>> x[99999]
Traceback (most recent call last):
File "<pyshell#21>", line 1, in <module>
        x[99999]
IndexError: string index out of range

The character 9 letters after the starting letter is just as non-existent as the 99,999th, so each request results in an IndexError.

How can we use these indices to actually show that strings are immutable? Well, remember the capitalize example from earlier in this section? We couldn't simply capitalize a string in-place because it would mean changing any other string with that same value. However, we can set our variable to point to a new value that corresponds to the answer we'd expect. If strings are not immutable, we should be able to change individual characters inside of strings. So what happens if we actually try to modify a single letter inside of a string? Let's try to capitalize a name.

>>> x = 'alexander'
>>> x[0] = 'A'
Traceback (most recent call last):
File "<pyshell#39>", line 1, in <module>
        x[0] = 'A'
TypeError: 'str' object does not support item assignment

Oops, again! In this case, we hit something called a TypeError. In particular, the error is complaining about item assignment not being supported. To paraphrase, we're not allowed to modify single letters in our string. You can't assign 'A' to the first position in the string, because the string is immutable! So how can we actually get the same effect as modifying a single character in a string?

A naive example is to break the string down into characters, and to change the single character that we're interested in modifying. Here's one very simple way to only change the first character to a capital letter.

>>> x = 'alexander'
>>> x[0].capitalize()
'A'
>>> x[0].capitalize() + x[1] + x[2] + x[3]
'Alex'

If we take each letter in the string, one-by-one, and concatenate (glue) them together using the plus symbol, the string can be broken down and reformed piece-by-piece. This is a new way to look at the addition symbol. It isn't the only way to do this, but it's here as a learning example. You won't need to do something this extreme each and every time you get a string from your users. It is important to know that strings are immutable objects, and that we can't just ask Python to capitalize the first letter and be done with it.

Reusing the plus symbol for string concatenation (again, you can read that as string joining or string gluing) is a convenient analogy for building new strings from the values in old ones. We can show some of the similarities between numbers and strings by example.

>>> 1 + 2
3
>>> "Py" + "thon"
'Python'

In each case, we obtain the result by adding the first value and the second value together to return a new value. In neither case do we actually modify either of the values. Instead, we take the values and combine them together to get something new. To show how strings and numbers are different, let's use a slightly different example.

>>> 1 + 2
3
>>> int(1 + 2)
3
>>> "1" + "2"
'12'
>>> int("1" + "2")
12

The strings "1" and "2" may look just the same as numbers 1 and 2 to us at first glance, but remember that they are significantly different. When we add (concatenate) the strings "1" and "2" together, we get the string "12". Even when we use the int built-in function, the concatenation of the strings "1" and "2" after converting to an int is still 12.

Slicing

Now we've seen an example of breaking down a string into its individual components and gluing it back together using the plus symbol. There are efficient ways of getting small portions of the string called substrings that might be more interesting. For that, we use a technique called slicing.

Given a starting index and an ending index, it is possible to specify the positions in the string to be returned. An index is a position in the string, like the first letter or the second last character. Slices are specified using the colon symbol, and obtaining a slice appears very similar to the string indexing seen above. Consider this example:

>>> x = "String Slicing"
>>> x[0:6]
'String'
>>> x[7:14]
'Slicing'
>>> x[0:3]
'Str'

We define a simple string in the x variable. First, we obtain a slice of x from the 0th index position and cut off right before the 6th index. A slice starts at the first position on the left side of the slice operator (the colon), and ends right before the position at the right of the slice operator. It's as if you put a stop sign in the position at the last index. The character at the 6th index in x is " ", and if we imagine taking some scissors to the left side of the 6th index, we're left with the single word "String" as our new sliced value. Visually, it looks like this:

0 1 2 3 4 5 6 7 8 9 10 11 12 13
S t r i n g     S l  i  c  i  n  g

Since we're cutting on the left side of our indices, getting the slice from 0 to 6 gives us the word "String", and getting the slice from 7 to 14 gives us the word "Slicing".

You might be asking yourself where the 14th index is, as it doesn't show up on our diagram above. Remember that we need to cut on the left side of the index. Really, any position after 13 is acceptable, because we cut off the string at the last letter. If you cut off my first name after 10 letters, it doesn't mean that my name is now "Alexander     ". My name is still "Alexander", regardless of how many letters after the final "t" you decide to cut. It is actually possible to take advantage of this fact by omitting either the left side or the right side when they are equal to the start or end of the string respectively. For example,

>>> x = "String Slicing"
>>> x[:6]
'String'
>>> x[7:]
'Slicing'

In the first example, we omitted out the left-hand side of the slice operation, where we'd previously had the 0 index. The left-side will default to the beginning of the string, so we can actually leave it out if we only want the first 6 letters. In a more convenient fashion, we can do the same thing with the end of the string. We could use x[7:100] if we wanted, by the logic that we used above to suggest that the end of the sliced string will be the end of the source string if we go too far. Rather than use large and silly numbers, we can just omit the right-hand side if we want to slice up to the end of the string itself. What if we omit both the left-hand side and the right-hand side?

>>> x = "String Slicing"
>>> x[:]
'String Slicing'

You might not be surprised to see the original string. If we read that out to ourselves, we've asked Python to take a slice of the string stored in x from the first position all the way to the last. Of course, in this case, we get the original string returned to us.

String functions

We've established that strings are immutable, and that we can slice out substrings and paste characters and other strings back together to make new ones. We looked at referencing individual letters using the square brackets, and we applied functions like capitalize to a string to get a new returned value in some other format. Let's take the function idea further, and see how we can operate in more interesting ways on the strings we've got.

Python offers a large number of functions similar to capitalize that allow you take a string as input and derive another new string (or several strings) based on the input. To begin, let's look at the upper and lower functions.

>>> x = "Alexander's String"
>>> x.upper()
"ALEXANDER'S STRING"
>>> x.lower()
"alexander's string"

Whereas we'd capitalized on the first letter in the entire string previously, the upper and lower functions provide the ability to completely convert a string to uppercase or lowercase. Keep in mind, we're not modifying the actual value stored in x, we're returning a new string that can be used later in our program. Just like capitalize, we'd need to use variable assignment if we wanted to store the result of one of these operations.

It is possible to determine whether or not substrings are found inside of another string using the find function.

>>> x = "Alexander's String"
>>> x.find("String")
12
>>> x.find("Alexander")
0
>>> x.find("Sam")
-1
>>> x.find("exa")
2

The find function returns the index of the first character in the provided substring, and if it's not in the string at all, the function returns -1. The reason we get -1 back is that -1 is always an invalid index. What is the negative first letter in your name? The use of -1 is common as an error code, particularly in string functions. When we look for the substring "Alexander", it returns a 0, corresponding to the 0th index. That's where the substring starts, and since we've got an index greater than -1, we know the original string contains our substring. The function even works when the substring isn't at the starting position in the string, as seen in the examples of "String" and "exa".

Strings are case-sensitive though, and if we try to look for "Alexander", we'll need to ensure that the string actually has "Alexander" with a capital A. Take a look at this:

>>> x = "Alexander's String"
>>> x.find("alexander")
-1

What happened there? As we mentioned, find is case-sensitive, so trying to find "alexander" results in a failure since our source string uses a capital letter. In strings, it is necessary to respect the capitalization of each letter. If we think back to our use of boolean values, and the equality operator, this can be verified:

>>> "A" == "a"
False

That tells us that Python doesn't treat a capital letter as being equal to a lower-case letter. If we want to check to see if a name is in the source string, we can do something a little more complicated. Look at this code, and before you read on, see if you can see exactly what it's doing and why it works.

>>> x = "Alexander's String"
>>> x.lower().find("alexander")
0

Now what have we done? We got a 0 back from our find function, and that tells us that we succeeded; we found the lower-case string "alexander" at index 0 in the string. But what string are we looking at? If you look carefully, we've taken the result of x.lower(), which is itself a string, and used that as input to the find function. To expand things out in more detail, take a look at the example here:

>>> x = "Alexander's String"
>>> x
"Alexander's String"
>>> x.lower()
"alexander's string"
>>> y = x.lower()
>>> y.find("alexander")
0
>>> "alexander's string".find("alexander")
0
>>> x.lower().find("alexander")
0

First, define the x variable, and store a mixed-case string in it. If we then use the lower function that we talked about earlier in this section, we get a lower-case representation of the string. As long as the substring that we're searching for is all in lower-case, we'll be able to find the word "alexander" that we're looking for. So exactly what is happening when we call the find function on the direct result of the lower function? We know that x.lower() is going to return a string, so it is possible to actually use that new string as the input for find. You can see this in action when we define the y variable and save the result of x.lower() in it. With y as input to find, we get the correct index. By explicitly calling find on a string, you might be able to see in greater clarity how a function like find is actually called on a string, and how find is applied to the source string with a parameter.

It's possible to chain these operations together like this, as we know that these particular string functions also return strings themselves. To use a silly example for emphasis, here's a line of code that I hope you never have to write.

>>> x = "ALEXander"
>>> x.upper().lower().capitalize()
'Alexander'

Each of the upper, lower, and capitalize functions all return a string themselves, so the output from each of those can be used directly as input to the next. We've chained them all together using the period symbol, and reformed the mixed-case input into a nicely capitalized string.

String formatting

To really get the most bang for our buck with strings, we'd like to be able to use them effectively in the construction of other strings. We've played around with this when using string concatenation, and printed out one string after another. There is a better, more Python friendly way of tackling this problem. It's called string formatting, and it uses a built-in function in Python called format.

The format function allows us to define variables inside of strings that can be swapped out for other values at a later time. If we know that we'd like to greet the user, but we don't yet have their name, we can define a greeting string to format properly once the user finally tells us their name. These format strings look a bit unusual, so let's look at an example to explain exactly what it's doing.

>>> name = "Alexander"
>>> age = 30
>>> greeting = "Hello {0}. You are {1} years old."
>>> greeting.format(name, age)
'Hello Alexander. You are 30 years old.'

So what the heck are those curly braces in greeting? And what do the numbers mean? These curly braces are references to variables that we'll pass in at a later time. You can think back to the positional character referencing that we used to find individual letters in strings. This is a similar idea. The {0} and {1} substrings are like variables inside of greeting. When we actually format greeting later, we can give it those two variables to help us build the string we'd like to use. Specifically,

>>> greeting = "Hello {0}. You are {1} years old."
>>> greeting.format(name, age)
'Hello Alexander. You are 30 years old.'

Each time that the format function runs into a substring of the form {#}, it looks at the parameters that have been passed to it and tries to fit in the appropriate value. It isn't limited to the order of the parameters or only using each variable once either; we can build strings with lots of replacements using format.

>>> name = "Alexander"
>>> age = 30
>>> formatString = "{0}{0}{0} {1}{1}{1} {0}{1}{0}"
>>> formatString.format(name, age)
'AlexanderAlexanderAlexander 303030 Alexander30Alexander'

We also don't need to worry about the separator, like we did with the print function, because we actually specify exactly how the string is going to look beforehand. In this example, formatString is a string template, and our string template is going to get formatted later to look exactly how we want it to look. There are no extra details like spaces in our original strings and no unnecessary separation character.

There is an optional variable identifier that can be provided for each of the format variable parameters. For example, if we know that we'll be passing in the user's name and age, we might do something like this:

>>> userString = "Hello {name}. You are {age} years old."
>>> userName = input("Enter your name: ")
Enter your name: Alexander
>>> userAge = int(input("Enter your age: "))
Enter your age: 30
>>> userString.format(name=userName, age=userAge)
'Hello Alexander. You are 30 years old.'

See what we did there? Instead of using the numerical index positions for the parameters in format, like {0} and {1}, we took a similar approach to the sep parameter in the print function, and explicitly defined replacement names in the template string. We said that there would be two variables that format was going to give us in userString. We expected to look for a name variable and an age variable in the actual value for userString, and in format, we asked the replacement function to use our userName variable wherever it asked for name, and our userAge variable wherever it asked for age. Once we did that, we got rid of the ugly {0} and {1} portion of userString, and made it a little more readable in the process.

The format function has a lot of additional power behind it, making it much more than just a simple tool for searching and replacing. Each of the replacement tokens in the original string can be tagged with information on exactly how to format the data being inserted in that string location. Let's say that we write a mobile application to bring to restaurants with us to help calculate the 15% gratuity we like to give to servers. With larger bills, or bills that aren't multiples of ten dollars, it might be tricky to ensure that our server gets an appropriate tip. We can write a simple program to calculate the answer.

>>> total = float(input("How much did the bill come to? $"))
How much did the bill come to? $34.75
>>> tip = total * 0.15
>>> print("A 15% tip is ${0}.".format(tip))
A 15% tip is $5.2125.

That value is certainly correct, as 15% of the float value 34.75 is 5.2125. However, it doesn't make much sense when we're dealing with money. We don't have fractional pennies floating around, and we can't tip five dollars and twenty-one and a quarter cents. Fortunately, we can use format to restrict the output to two decimal places. This technique is called format specification. Format specification gives us a way to provide a variable for substitution into our original string, along with the ability to actually format the output given a set of rules about how it should look on the screen. Let's look at a few examples.

>>> print("A 15% tip is ${0}.".format(tip))
A 15% tip is $5.2125.
>>> print("A 15% tip is ${0:.1f}.".format(tip))
A 15% tip is $5.2.
>>> print("A 15% tip is ${0:.2f}.".format(tip))
A 15% tip is $5.21.

Python's format specification rules allow us to add a bit of additional information to the {0} substitution symbol, in cases where we want to round to two decimal places or make other subtle display tweaks. The colon tells Python that we've provided a variable that we'd like to display, but also that we're going to ask for a little extra help when printing it. If Python runs into a period after the colon, it looks at the number that immediately follows it, and optionally, a letter indicating the variable type that we should treat the data as. When the letter is f, it means we've got a float number type. In that case, the number corresponds to the number of decimal places we'd like to print. Given that information, we can read {0:.2f} as "Print the variable at index 0 (the tip variable, in this case), and format it as a float number rounded off to two decimal places." You can see how changing the number also changes the number of decimal places.

Format specifications aren't just limited to decimal places. For example, if you'd like to format your information on the screen like a spreadsheet, where all the data in a column has a fixed width, you start off the format specification with information about how many characters across you'd like your data to take. You can also specify whether the text is left-aligned, right-aligned, or centered within the character spacing you specify. Think of the format specifications as a list of individual specifications rather than a single mysterious block of strange characters.

>>> print("A 15% tip is ${0:<10.2f}.".format(tip))
A 15% tip is $5.21      .
>>> print("A 15% tip is ${0:>10.2f}.".format(tip))
A 15% tip is $      5.21.
>>> print("A 15% tip is ${0:^10.2f}.".format(tip))
A 15% tip is $   5.21   .
>>> print("A 15% tip is ${0:@^10.2f}.".format(tip))
A 15% tip is $@@@5.21@@@.

In the first example, it might help you to think about three distinct pieces of data. First, the index of the variable that we're talking about. We've used that one, and in this case, it's just the single number 0 at the very left-most side of the format specification. The last piece of data in the format specification is the one we just looked at, beginning at the period, and ending with the character f. That block tells us that we're going to give format a float, and that we should round it to two decimal places.

In the middle of those two is a width and position specification, and in the first example, it is given as "<10". When specifying the width and orientation of the variable in format, you provide the minimum number of characters that the variable is going to take up in the new string. In the example above, the value 10 tells format that our tip variable is required to take up at least 10 display characters. That explains why our variable has a bunch of extra spaces on either side, depending on how we orient the data. The width is used in conjunction with a positional symbol. If we use the less-than symbol, which happens to look like an arrow pointing to the left, we tell format that the data should be oriented on the left side of the 10 character minimum width. The greater-than symbol, which happens to look like an arrow pointing to the right, does the same thing for the right side. The caret (or hat, or maybe circumflex, but specifically the character ^) looks sort of like an arrow pointing up, and means that we should center the data in the 10 character width. If we give a symbol before the positional specification instead of spaces, format will fill in the extra space with the new character.

Breaking Stuff

This section featured a lot of broken code already to show you some of the valid and invalid ways to use string values. Let's break a few more things while we're here.

The format function is an incredibly useful way to build custom strings. An earlier example showed how you could build a string from the name and age of the user. By providing variable names inside of the string we want to format, we can specify those names later in the format function to customize things appropriately. Here is the specific code, one more time:

>>> myString = "Hello {name}. You are {age} years old."
>>> userName = input("Enter your name: ")
Enter your name: Alexander
>>> userAge = int(input("Enter your age: "))
Enter your age: 30
>>> myString.format(name=userName, age=userAge)
'Hello Alexander. You are 30 years old.'

What happens if we forget to specify one of the variables? Can we format a string that specifies replacements without actually providing the replacements?

>>> myString = "Hello {name}. You are {age} years old."
>>> myString.format(name="Alexander")
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
    myString.format(name="Alexander")
KeyError: 'age'

Negative. In the source string, we explicitly declared that if this string was going to be formatted at some point, it was going to have to be done with both the name and the age variables provided. Without all of the replacement variables, Python throws up its hands with a KeyError exception.

We haven't talked about keys yet; those are dealt with in more detail when we talk about the dictionary data type. You can treat them as being similar to variables for the moment. We speak about key-value pairs, where the key is an identifier similar to a variable name, and the value is the actual object referred to by the key. In myString above, there are two keys that are declared. If one (or more) of the keys are not associated with a value when format is called, Python throws a KeyError exception. You'll see the same thing in the implicit format strings, like this:

>>> myString = "Hello {0}. You are {1} years old."
>>> myString.format("Alexander")
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
    myString.format("Alexander")
IndexError: tuple index out of range

In this example, the specific error is different, but it's the same kind of flaw. When Python complains about an IndexError, it's telling you that the value at index 1 (the second parameter passed to format, since we start counting at 0) doesn't exist, or is "out of range". If you start to see KeyError or IndexError exceptions with your format strings, make sure you're providing all the necessary values for your string!

Summary

Python string objects allow you to manipulate text in your programs. They come with a powerful set of built-in functions for manipulating and formatting data. Strings can be easily converted to other data types, but only if the data inside the string makes sense for the type you are converting to.

Try playing around with a few strings. Set up some templates, see if you can get some input from the keyboard, and experiment with rendering data to the screen.

Exercises

1. Write a program to ask the user for some information about themself, such as their name, hometown, favourite colour, and so on. Use print statements and the format function to tell them what they said.

2. Ask the user for three or more strings, and print them out as if they were in a spreadsheet column with a fixed width. For example, your program might output the following:

Enter your first name: Alexander
Enter your last name: Coder
Enter your cat's name: Ulysses

    NAMES
----------
    Alexander
    Coder
    Ulysses

3. Write a program to calculate the total tax where you live. Allow the user to type in a dollar amount (just a float or even an int, and don't worry about the currency symbol), and calculate the tax based on your local tax rate. For example, if my total tax on a product is 13%, I would calculate 13% of the total cost of the item and print it to the screen.

4. Build a Mad Libs program that asks the user for a few words and then builds a sentence. For example, your program might display the following output:

Enter an adjective: funny
Enter a noun: cat
Enter a verb: dance

The funny cat likes to dance.