Skip to content

Strings and Lists

Strings

As you might be aware, strings are one of the basic types in python. Strings got their name from the idea that a single character is a basic type in most programming languages, and if you string many of them together you get a string! So a string is little more than an array of single characters all grouped together to form a word or a sentence. Declaring a string in python is really easy, as shown in values and types, but it's also important to know that strings are essentially a list of characters in programming because there are some cool things we can do with that knowledge.

f strings

F strings are the way we format strings in the most recent version of python. By using f strings we can insert variables into our strings and even evaluations directly into the string to be printed. Have a look at this code:

example-1.py from the beginning to the end
#!/usr/bin/env python3
# Python program to show f strings


# Some types of strings are worth noting in Python. 
one_line1 = "this is a string"
one_line2 = 'this is a string too'
one_line3 = "this is a 'string' and is only possible with mixed ticks"
one_line4 = 'this is a "string" too with mixed ticks'
multiline1 = " \
    you just need to use the \\\
        also known as the escape character \
            lets you stretch things across lines in the code, \
                not in the output though without the \\n character \n \
                see!!"
multiline2 = """ You can also use this with 
or without the escape character
    \
    see!!"""
multiline3 = '''
Single ticks to make a multi line comment too!!  
It's quite intuitive once you get used to it.
And it's super handy for making your own banners.
'''


# Just a list to print out all of the strings... nothing to see here. 
list1 = [one_line1, one_line2, one_line3, one_line4, multiline1, multiline2, multiline3]
for i in list1:
    print(i)


# This is the older way of using string formatting using .format
# it's still perfectly valid to use, so use whatever you
# prefer, but it may become depreciated in future. 
print("Hey, what is your name? ")
name = input()
print("Hi {}, it's great to meet you!!".format(name))


# You can also use this with numbered 
# positional arguments to change the order, consider this code.
print("Hey, what is your name? ")
name = input()
age = input("How old are you? ")
print("Hi {1} aged {0}, it's great to meet you!!".format(age,name))


# We can also do some things with .format() and dictionaries
person = {'name': name, 'age': 104}
print("Whazzzuuuup!! How you doing {name}, brah you're like soooo old at {age}".format(name=person['name'], age=person['age']))
print("Hi {name}, wow you're super old!! How are you still alive at aged {age}".format(**person))  # this does dictionary unpacking.


# So the reason we don't use .format() anymore is because it is so verbose.
# When you start to get long strings with a number of complex variables 
# added to it then you can see how it can get unwieldy. This is where f strings
# come in. 
not_silly_string = 'F string'
not_past = "future"
print(f"{not_silly_string}'s are the {not_past}!")
print(f"""{name}""")


# You can also do evaluations in f strings and it'll still handle them, and 
# stuff with lists and dictionaries.  
print(f"Your age is {person ['age']}")
print(f"You surely won't last another {int(age) + 10} years!") 

Example

Here you can see lots of ways to format strings, this includes some of the older ways to do it but mostly all of the new ways. You may come across original python formatting with characters such as \t, \n, and \r too. They are tab, newline, and return respectively.

If you run this example, it's pretty basic, but it shows how inputs and strings are used to create interactive programs using f strings. There are some different conventions for how to write multi-line strings too. As a rule, it doesn't matter which style you decide to use for string formatting or multi-line strings, as long as it's consistent throughout the code.

There are also variables we place in the f strings to format them with the values stored in those objects.

As you can see, f strings are much more concise and it's much easier to understand what's going on at a glance because it's all happening in the string where you've told it to happen. The .format() method has everything at the end of the string so you have to dart backwards and forwards in the string to understand everything that's going on. So for this section when we talk about formatting strings we'll use the f string format, but it's worth noting that there is usually a .format() equivalent.

Formatting your strings

There are lots of things that you might want to do with your output when using strings. You might want to make spaces, funky layouts and all sorts of interesting things. You might also want a print out to show the output of function that you're not sure what it will return, and so need dynamic output. In all of these cases f strings has got your back. Consider this code:

example-2.py from the beginning to the end
#!/usr/bin/env python3
# Interesting examples of string formatting 

import random 

# This example shows direct evaluation inside the string at runtime
print(f"This is an evaluation: {(57 * 126) % 4}")
var1 = 9 
var2 = 11
var3 = 22
print(f"This is also an evalutation but we use the variables instead: {(var3 * var2) % var1}")


def function_of_randomness():
    return( random.randint(1, 10) )

print(f"This is also an evaluation using functions: {function_of_randomness() * function_of_randomness()}")



# f strings can also take in objects to use in evaluations, if you're unsure about objects then see the 
# Object Orientation section. For an f string to take in the object it must have either a __str__ or __repr__
# method defined. 
class User():
    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation

    def __repr__(self):
        return(f"{self.name} is a {self.occupation}.")

u = User('Freddy Mercury', "Singer/songwriter") 
print(f"The output of our class object can also be evaluated in the f string: {u} \n") 



# This is direct formatting with f strings to influence outputs
# Starting with formatting decimal points in floating point numbers
val1= 9 / 2 
print(f"We can format floats to have different numbers of decimal places: 5 = {val1:.5f}, or 2 = {val1:.2f}. \n")

# We can also influence columns and column widths with f strings
# check the iteration section if you're not happy with 'for' and 'while' loops
for i in range(1, 11):
    print(f"{i:02} {i * i:3} {i* i * i:4}")  # 02 pads a zero in front of the number

# You can also print out the characters in different formats
for i in range(1, 17):
    print(f"Decimal: {i} Octal: {i:o} Hexadecimal: {i:x} Scientific: {i:e}")

# We can also justify the the f string brackets 
# < left, > right, ^ centered
print(f"{'Decimal': <10}{'Octal': ^10}{'Hex': ^10}{'Scientific':>10}")
for i in range(0, 17):
    print(f"{i:-<010}{i:^10o}{i:^10x}{i:>10e}")  # The dash(-) pads the first entry with dashes.



# Combining a lot of the above, we can create something a bit nicer...
names = ['Raj', 'Shivam', 'Shreeya', 'Kartik'] 
marks = [7, 9, 8, 5] 
div = ['A', 'A', 'C', 'B'] 
id = [21, 52, 27, 38] 

# Creating the headings 
print(f"{'Name' : <10}{'Marks' : ^10}{'Division': ^10}{'ID': >5}")

# filling out the table using looping 
for i in range(0, 4):
    print(f"{names[i]:<10}{marks[i]:^10}{div[i]:^10}{id[i]:>5x}")

Example

In this example we take the previous example of variables used in strings and take it up another notch. You can directly evaluate a lot of things in an f string, including variables, mathematics on it's own, functions, mathematics with variables and functions, classes, lists, dictionaries... and this list is not exhaustive.

As part of this example we also look at the ways we can format f strings to include alignment, padding, and some basic conversion to octal and hexadecimal.

F strings are also the fastest implementation of string formatting that python has seen, you can find more on this and another take on some of our examples here

strings as a list of characters

We've mentioned it before in these docs, but a strings object in python is a just a fancy list of characters and so we can do manipulations to a string like we would a list or tuple. Nearly all the things we can do to lists and tuples we can also do to strings, so it's worth mentioning them together to understand how they can both behave.

Sequences

A list of characters, a list that contains a bunch of data, or a tuple, are all sequences of data. Although, those aren't the only sequences in python, to avoid confusion we'll stick to these as they are the most commonly used. We've looked at how to create each of them in the basic types portion of the documentation, and how to use them in loops to count through the data in them, but there are many other things that can be done with sequences.

Slicing

Slicing is the action of taking a part of a list to be used in another area of code or put into a variable. Slicing is most often done to lists, but this index ranging works on strings and tuples, and has a generalised form of:

list[start:stop]    # items start and go through till stop -1
list[start:]        # items start and go through till the end
list[:stop]         # items start at the beginning until stop
list[:]             # is a copy of the whole list
list[start:stop:step]   # items start and go till stop -1, by step

Take a look at this piece of code:

example-3.py from the beginning to the end
#!/usr/bin/env python3
# Example containing sequence manipulation

# slicing simple strings
slice_string = "banana"
print(slice_string[3:])
print(slice_string[:3])
print(slice_string[1:6])
print(slice_string[1::2])
print(slice_string[::-1])  # reverses the string

# slicing the same using a list instead
slice_list = ['b', 'a', 'n', 'a', 'n', 'a']
print(slice_list[3:])
print(slice_list[:3])
print(slice_list[1:6])
print(slice_list[1::2])
print(slice_list[::-1])

# slicing using tuples 
slice_tuple = ('b', 'a', 'n', 'a', 'n', 'a')
print(slice_tuple[3:])
print(slice_tuple[:3])
print(slice_tuple[1:6])
print(slice_tuple[1::2])
print(slice_tuple[::-1])

Output
ana
ban
anana
aaa
ananab
['a', 'n', 'a']
['b', 'a', 'n']
['a', 'n', 'a', 'n', 'a']
['a', 'a', 'a']
['a', 'n', 'a', 'n', 'a', 'b']
('a', 'n', 'a')
('b', 'a', 'n')
('a', 'n', 'a', 'n', 'a')
('a', 'a', 'a')
('a', 'n', 'a', 'n', 'a', 'b')

Example

You can see in this example that slicing, with any sequence type, is the same format as the generalised form suggests. In each basic type we are using the exact same data and slices, the only difference in the output is in the way the basic types are printed out to the command line.

Play around with these examples and come up with more until you are happy with slicing sequences. It's worth noting that negative and positive indices are allowed throughout the slicing syntax too. So try out as many variations as you can think of.

Methods

Sequences, as they are objects in python, also have common methods that can be called too. Each of these basic types in python have their own methods, so we'll go through some of the common ones you will see in lists and strings and then talk about the common ones you will see that are common to all sequences. All methods used will come in the same format roughly variable_name.method(options).

String methods

There are loads of ways to manipulate strings in one way or another, a much more exhaustive list can be found in the python docs or here. Take a look at this code to see how some of the more common string methods are used:

example-4.py from the beginning to the end
#!/usr/bin/env python3
# program to show you common string methods

string_to_method = "    This is our string to change!!... I hope you like it!!!     \n"

print(
    string_to_method.strip(), '\n',     # strips the whitespace and newlines from around the string
    string_to_method.capitalize(),      # Capitalises the whole string
    string_to_method.upper(),           # Capitalises the first letter of the string (side effect: permanently changes the string and doesn't print)
    string_to_method.rstrip(), '\n',    # Strips all of the whitespace from the end (right side) of the string.
    string_to_method.lstrip()           # Strips all of the whitespace from the left side (beginning) of the string. 
) 

A good list of string methods can be found here: http://www.python-ds.com/python-3-string-methods

List methods

append

This method is used to add items to a list. See the example below:

>>> a=[2,4,6,8,10]
>>> a.append(12)
>>> a
[2, 4, 6, 8, 10, 12]
>>>

index

Returns the position of the first element to match. Throws an exception if it is not found.

>>> a
[2, 4, 6, 8, 10, 12]
>>> a.index(2)
0
>>> a.index(10)
4
>>> a.index(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 3 is not in list
>>>

pop

Returns the last item in the list and removes it from the list.

>>> a
[2, 4, 6, 8, 10, 12]
>>> a.pop()
12
>>> a
[2, 4, 6, 8, 10]
>>> b=a.pop()
>>> b
10
>>> a
[2, 4, 6, 8]
>>>

remove

Removes the first instance of a given item from the list. Throws an exception if the item is not present.

>>> a
[2, 4, 6, 8, 2, 4, 6, 8]
>>> a.remove(4)
>>> a
[2, 6, 8, 2, 4, 6, 8]
>>> a.remove(9)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
>>>

Others

A good list of methods, along with examples, is given here: https://www.geeksforgeeks.org/list-methods-python/

Methods for tuples

Coming Soonish

List Comprehension

Coming soonish

Test Your Understanding

Try these exercises to test your understanding. They begin using only basics, but progressively require more of the concepts and functionality described above.

Class Register

Write a python program that includes the following list:

pupils=["Amal", "Amani", "Chadi", "Farah", "Faris", "Ikram", "Imani", "Ismat",
"Laden", "Malak", "Nakia", "Adar", "Adi", "Almog", "Amit", "Ariel",
"Aviv", "Bar", "Michi", "Mikoto", "Mirai", "Mitsuki", "Nagisa", "Nao",
"Naomi", "Natsuki", "Nikko", "Rei", "Ren", "Riku", "Rin", "Brune",
"Cocky", "Conny", "Cox", "Daan", "Demy", "Didi", "Dien", "Jesse",
"Jo", "Jocelyn", "Joey", "Jordan", "Jude", "Justice", "Kary", "Kay"]

Now use a loop to output a roll-call. For example, the first line will be "Amal, are you here?", followed by "Amani, are you here?".

And the total is... (lists, iteration)

The code below has a function called total that currently returns 0. Change it so that it returns the total of the numbers in the list passed to it.

totaliser.py from the beginning to the end
#!/usr/bin/env python3

def total(items):
    # Need to loop through the items and add them together
    # Remember the concept of an accumulator?
    return 0

numbers=[12,19,121.2,14,99]


print("List of numbers")
for n in numbers:   
    print(n)

print(f"\nAnd the total is: {total(n)}")
Output
List of numbers
12
19
121.2
14
99

And the total is: 0

Types (lists, iteration)

The function in the code below will return a description of the type of thing passed to it.

type_example.py from the beginning to the end
#!/usr/bin/env python3

def typeStr(thing):
    return type(thing).__name__


a="Hello!"
b=12
c=12.5
d=True
e=dir
print("Some Types")
print(f"a is of type {typeStr(a)}")
print(f"b is of type {typeStr(b)}")
print(f"c is of type {typeStr(c)}")
print(f"d is of type {typeStr(d)}")
print(f"e is of type {typeStr(e)}")
Output
Some Types
a is of type str
b is of type int
c is of type float
d is of type bool
e is of type builtin_function_or_method

Create a list called stuff that contains a number of items of different types. Now use a loop to display similar output to the given example, but for all items in the list, regardless of how many there are.

Bad Apples (lists, iteration)

Write a function that accepts a list and checks to see if each item in the list is "apple". If so, return -1. Otherwise, return the position of the first item that is not "apple".

Language Filter (strings, searching, replacing)

Complete the code given below, so that the test function returns True if the given "bad word" is found, and the filter function returns the input with the bad word replaced by @*!#.

badwords.py from the beginning to the end
#!/usr/bin/env python3

def test(data, badWord):
    ### Your code here
    return False


def filter(data, badWord):
    ### Your code here
    return data

if __name__=="__main__":
    someText="As a Vi user, I enjoy random text being entered in my code and getting stuck in my editor."

    if test(someText,"Vi"):
        print("A bad word was found in the text. It has been sanitised.")
        print(filter(someText, "Vi"))
    else:
        print("No bad words were found.")
        print(someText)

Language Filter 2 (strings, searching, replacing, length)

Enhance the previous version of the language filter so that the replaced text is a sequence of stars of the length of the replaced word. Example: My name is ****.

Language Filter 3 (strings, lists, iteration, searching)

Enhance the filter and test functions so that:

  1. Test accepts a list of bad words instead of one string. It returns a list containing the bad words found, but only once per find. Example: test("I don't give a gosh darn who you darning well are", ["flipping","gosh", "darn"]) would return ["gosh","darn"]. If no bad words exist, it returns an empty list ([]).
  2. Filter also accepts a list of bad words and replaces them with sequences of stars of the right length. For example: test("I don't give a gosh darn who you darning well are", ["flipping","gosh", "darn"]) would return "I don't give a **** **** who you ****ing well are"

Half and Half (lists, slicing)

Complete the code below. There are three places to complete.

  1. The mean function should return the mean of the given list
  2. The largest function should return the largest value in the given list
  3. The stats function is complete, but requires that you properly divide the given list of numbers into two parts, left and right.
half.py from the beginning to the end
#!/usr/bin/env python3
import random

def mean(data):
    #YOUR CODE HERE
    #Find the total of the items and divide by the number of items
    return 0

def largest(data):
    #YOUR CODE HERE
    #Return the largest/highest/max item in the numerical data
    return 0

def stats(data):
    #YOUR CODE HERE
    left=[] # replace with first half of the list
    right=[] # replace with second half of the list

    print("First Half\n----------")
    print(left)
    print(f"Max : {largest(left)}\nMean: {mean(left)}")
    print("\nSecond Half\n-----------")
    print(right)
    print(f"Max : {largest(right)}\nMean: {mean(right)}")

    print("\nComplete\n--------")
    print(data)
    print(f"Max : {largest(data)}\nMean: {mean(data)}")


if __name__=="__main__":
    data=[]
    for i in range(20):
        data.append(random.randint(1,99))
    stats(data)