Strings

0. Learning objectives


1. Strings

How would you define a string like Hello World!? It is just a sequence of characters. For Python, strings are sequences of characters enclosed between either single or double quotes (as long as they match at both ends of your string). So, if you want to define the string above, you can do it either as "Hello World!" or 'Hello World!'. Try to define a few strings in Brython! You can also include spaces and special characters in your strings. You just have to be careful when you use quotes...

If your quotes are not matching, you get a SyntaxError: EOL while scanning string literal which means that Python didn't find the matching quote before reaching the end of the line.

As long as your quotes at the beginning and the end of the string match (two single or two double), you can include the other quote inside your string. For example "Com'on! everybody knows that the Earth is round!" and 'You are in CS? You should really watch the "Coded Bias" movie!' are both legitimate strings. If you don't believe me, you can always check with Brython. (By-the-way, you should really watch Coded Bias).


Just in case you want to play with strings at lunch!

But what if you want to have both types of quotes within a string? You can do that by using what is called an escape sequence. An escape sequence uses a special character to let Python know that the following character should not be interpreted using the usual syntax rules. In other words, we are escaping for a moment the interpretation rules, but just for the following character. In Python all the escape sequences start with the backslash character (\). Using escape sequences, we can create strings like "I'd love to hear you say \"hello\" in Arabic". Because we escaped the double quotes inside the string, Python treats them as regular characters instead of special one indicating the beginning or end of a string. (Try to write the string above without the backslashes in Brython and see what happens!) You might notice that, when you enter that string in Python, the interpreter might switch the quotes used to define the string. That's fine since it it not really changing your original string, just the way it is represented.

What if you want to include an actual \ in your string? Well.. you can escape the backslash that using a backslash as well: "The backslash character looks like this: \\.". Remember, the escaping only applies to the character following the \.

There are several escape sequences that Python recognizes:

Escape sequence Description
\' Single quote
\" Double quote
\\ Backslash
\t Tab
\n New line
\r Carriage return

The new line (\n) is very commonly used since it can be used to send a piece of your string to the next line when it is displayed (e.g. "Hello\nWorld!").

These escape characters are rendered (generated) when the string is displayed on the screen using the print() function. Try to use all the example we have seen so far as arguments for the print() function in Brython! (e.g. print("Hello\nWorld!")).


2. Operations with strings

Great now that we can define strings, what can we do with them? (You should try all the following examples in Brython... it's not hard, it is at the bottom of your screen!) .

One thing that we can do, since a string is a sequence of characters, is to use the function len() (remember that you can get more information about a function using the help() function - help(len)) to tell us how long the string is (e.g., len("Welcome to CSCI0145!")). What is the length of the empty string ""?

We can also concatenate strings using the + operator. Try "How do you " + "concatenate strings?" in Brython and see what the result of this expression is. One thing you have to be careful when using the + operator is that it will only work if its behavior is define based on the operands. For example, try to add a string to a number ("I like the number " + 3). Python is going to complain because it first saw a string so it is expecting the second operand to be a string as well and, when it finds a number, it doesn't know how to handle it. What does the error say? The same is true if you try to add a string to a number (3 + " is a cool number!"). Once again, Python doesn't know how to handle it, although it notify us using a different error. Which one?

To solve this problem, you can convert a number to a string using the str() function: str(3) + " is a cool number", as we saw when we were discussing types and expressions. To add a couple of useful functions to the conversion-between-types family, you can use int() and float() to convert strings into numbers (e.g. int("32"), and float("37.2") will both convert the strings into an integer and a floating point number respectively). Try the following conversion in Brython to get a feeling about how these function work and where they don't.

As an alternative to converting between types, if you are only interested in displaying the result for humans and not in creating and actual string that you will need later for further computation, you can use the print() function to combine multiple objects you want to display. If you pass more than one argument to the print() function, it will display them one after the other separated by a space and terminated by the new line character.

The arguments don't even have to be of the same type. Try out the following:

Take a look at help(print) to see how you can modify the default behavior: you can, for example, change the character used to separate the arguments when they are displayed (sep=' '), or change the termination character (end='\n').

>>> help(print)
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) 
    
    Prints the values to a stream, or to sys.stdout by default. 
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout. 
    sep:   string inserted between values, default a space. 
    end:   string appended after the last value, default a newline. 
    flush: whether to forcibly flush the stream.

The arguments sep and end highlighted above have assignment statement in the function header! These types of arguments, known as keyword arguments allow you to define default values for some of the arguments in your functions. If you want to overwrite those default values, you can call the function and specify the argument using its name. For example, try print(3, "is my favorite number!", sep="---", end=" not!")

We will not mess around with file and flush since they determine where and how things are display and, right now, we are happy with seeing printed values show up where they do, but feel free to play around with sep and end.

Back to operations with strings, another thing that you can do is multiply a string by an integer. This results in the string being repeated that many times. Try, for example "echo... " * 3 or 5 * "long echo... ". Here is a cool one. What do you think will happen if you multiply a string by 0? (Think before trying it out in Brython).

Since strings are values, you can assign them to variables: hello = "Hello World!" and all the operation that we have seen can be applied to the variables containing strings.


3. Indexing

So far, we've been doing some simple operations with strings, but there is more! What if we want to extract a portion of a string? Or, what if we want to reverse a string? This brings up the idea of "iterating" through the characters in a string, which will build up to a more general approach when we discuss about iteration and lists.

Let's start off by answering the question: how do we retrieve a character at a particular index (location) in the string?

This index is like the "address" of the character in the string, starting from the left. We can retrieve a character at a particular index using square brackets, typically after the variable name containing the string.

Consider the string "Do you speak whale?", stored in a variable called message. If you think about every character being stored in its own little box within the larger box for the variable message (I know... it sounds like Python is all about boxes), and each little box getting a number starting from 0 for the box to the very left, then each character can be accessed with the appropriate index - the box number. In general, we can retrieve a character at a particular index using message[index] where index is an integer.



Try typing in message = 'Do you speak whale?' in Brython. As we have seen, if you type len(message), you will get the length of the string (19). Notice that the index only goes up to 18. This is because Python (like many other languages such as C and Java) starts indexing at 0. If we start counting at 0, the last index in a string s is len(s) - 1. If you try to access message[19] (or any number greater than len(s) - 1), Python will give you an IndexError since you are out of the bounds of your string.

What happens if we pass a negative index into the string? It turns out that this allows you to index the string in reverse order. In other words, a negative index will start the indexing at the end of the string (starting at -1 instead of 0 (which is the beginning of the string). For example, message[-1] will retrieve the question mark ? and message[-6] will retrieve the w.



So, if you are given a (negative) backward index j (starting from the right), what is the equivalent forward index (starting from the left)?

j is a negative integer and i is a positive integer. If they both refer to the same location in the string, from the figure above, you can see that the relationship between i and j satisfies i - j = len(message), therefore i = len(message) + j: message[i] = message[len(message)+j] (remember that j is negative).


4. Slicing

So we can extract a particular character from a string. That's cool! But what about extracting a portion of a string? For example, what if we just want to extract the word whale from our previous message? We can do this by slicing the string. Instead of using a single index, we can use the following syntax to extract aa substring from some original string stored in our variable message:

message[start:end:step]

where start is the index we want to start the extraction at, end is the index in which we will terminate the extraction (not including the character at this index) and step is the number of steps we take before we grab the next character (a step of 2, for example, will grab every other character between start and end - not included).

You don't always have to use start, end, and step. If you omit them, Python will use the defaults of start = 0, end = len(message) - 1 and step = 1 if you are going forward. If Python detects you are slicing in reverse order (step = -1), the defaults will be start = len(message) - 1 and end = 0. In other words, it will go backward.

You can also omit the step, and only using a single colon to slice. In this case, step will be assumed to be 1. For example, message[13:] will give you whale?. What happens if end < start? You should see an empty string ''. (This will also happen if the end > start and the step is negative).

One way to think about it is:

Python will try to go from start to end using the step. If it can do it, it will give you a string made of the characters it collects, if it cannot, it will give you an empty string.

Also, when you are slicing, Python will not complain if end is passed the end of the string. For example message[:40] will simply return you the whole string "Do you speak whale?".

Think about these examples and then try them out in Brython!

  1. message[13:18]
  2. message[11:6]
  3. message[11:6:-1]
  4. message[::-1]
  5. message[:5] + message[5:]
  6. message[1::2]

  1. 'whale'
  2. '' (an empty string)
  3. 'kaeps'
  4. '?elahw kaeps uoy oD'
  5. 'Do yo'
  6. 'oyusekwae?'

Here are some more examples that you can play with. Try to guess the outcome before you run the code.