Exploring the Demo Code

Our example program

Let’s look at an example program, and examine in detail what it is doing and how it works.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        album.position,
        album.title,
        "BY",
        album.artist,
        album.previousPosition,
        album.numWeeks
    )

Importing the PythonTop40 module

The first line in our program

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        album.position,
        album.title,
        "BY",
        album.artist,
        album.previousPosition,
        album.numWeeks
    )

uses the Python import command to bring the Top40 class from the top40 module into our code.

This import command means that our program can now use the Top40 class, to get the list of Top 40 singles and albums. The import command is how we tell Python that we want to use a feature that isn’t included in the Python standard library.

Creating a Top40 instance

The next line in our program creates a variable called top40 which becomes the way we will talk to the remote server where the lists of Top 40 singles and albums information is held.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        album.position,
        album.title,
        "BY",
        album.artist,
        album.previousPosition,
        album.numWeeks
    )

We can think of the top40 variable as providing us with a number of ways to access the Top 40 charts for albums and singles.

top40 does this through a number of properties that each returns different results to our program.

If we were to use the top40.singles property instead of the top40.albums property, then as you might expect our program would receive a python list of singles instead of a list of albums.

Other properties that we could use are top40.singles_chart and top40.albums_chart which both return a little bit more information about the chart itself - such as the date it was published and the date it was retrieved from the server.

Retrieving the Top40 albums

The following line of code creates a variable called albums and assigns to it the value returned from the top40.albums property.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        album.position,
        album.title,
        "BY",
        album.artist,
        album.previousPosition,
        album.numWeeks
    )

When this piece of code is executed, behind the scenes our top40 variable magically makes contact with a server over the Internet, asks it for the list of the Top 40 albums, and returns this list list of information to our albums variable.

The format of the returned data

If we could see the value returned to the albums variable in the above code, it would look something like this.

albums = [
    Entry(
        position = 1,
        artist = "One Direction"
        ...
    ),
    Entry(
        position = 2,
        artist = "Ed Sheeran"
        ...
    ),
    Entry(
        position = 3,
        artist = "Sam Smith"
        ...
    )
]

Note

The ... in the above example shows that there are more pieces of information in the Entry, but these are not shown to make the example easier to understand.

The data is enclosed in [] square brackets, which tells us that we have a Python list of ‘things‘. But what are the things in the list? Well, because we have a list of things, we can access the first (or 0 th item) in the list by placing ``[0]` after the name of a list.

print(albums[0])
Entry(postition = 1, artist = "One Direction"...)

Accessing the information within each chart entry

This tells us that we have a list of things of type Entry. There is one Entry for every album in our Top 40 chart. The example data above only shows the first 3 entries, but given that this is the Top 40 we are dealing with, we would expect to see 40 entries in our list.

Each entry is represented by a Python object called Entry. The Entry class has been created as part of the PythonTop40 project to hold the details of albums or singles in the chart.

As you’d expect from looking at the example code, the Entry class can hold information about the position of this entry, the name of the artist, the title of the album or single.

In addition, the number of weeks the album or single has been in the chart is accessed via the numWeeks attribute and the position that the entry occupied last week can be found by using the previousPosition attribute.

So in our original example, the next part the code loops through each of the album entries in the chart using the for statement, and then inside the loop, the value of album is set to each of the albums in our list.

This means that we can use the print() function to print the position, title and artist of each of the albums in our chart.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        album.position,
        album.title,
        "BY",
        album.artist,
        album.previousPosition,
        album.numWeeks
    )

Printing extra information about the chart entry

If we wanted to extend our demo program to print the number of weeks that the album had been in the chart, as well as the chart position it occupied in the previous week’s chart, we could do this by accessing the numWeeks and previousPosition attributes respectively.

The following code would achieve that.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
            album.position,
            album.title,
            "BY",
            album.artist,
            album.numWeeks,
            album.previousPosition
    )

If this code is run, it would result in something similar to this.

1 Never Been Better BY Olly Murs 1 0
2 X BY Ed Sheeran 23 2
3 FOUR BY One Direction 2 1
4 In The Lonely Hour BY Sam Smith 27 3
5 The Endless River BY Pink Floyd 3 4
6 Wanted On Voyage BY George Ezra 22 8
.
.
.
40 The London Sessions BY Mary J. Blige 1 0

Formatting the output columns

It’s not easy to see the information, but you can now see that there are two numbers at the end of each line that represent the numWeeks and previousPosition attributes respectively.

So if we now wanted to make the formatting a little easier to read, we can make use of the format() function that allows us to carry out formatting on a string. The description of the format() function is outside the scope of this tutorial, but hopefully the following code will be relatively simple to follow.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        "{:5} {:50} by {:50} {:5} {:5}".format(
            album.position,
            album.title,
            album.artist,
            album.numWeeks,
            album.previousPosition
        )
    )

When this code is run, it produces a column-based list of album entries that is much easier to understand.

 1 Never Been Better                                  by Olly Murs                                              1     0
 2 X                                                  by Ed Sheeran                                            23     2
 3 FOUR                                               by One Direction                                          2     1
 4 In The Lonely Hour                                 by Sam Smith                                             27     3
 5 The Endless River                                  by Pink Floyd                                             3     4
 6 Wanted On Voyage                                   by George Ezra                                           22     8
 .
 .
 .
40 The London Sessions                                by Mary J. Blige                                          1     0

Hopefully you can see that the format string features a series of place markers - represented by the {} braces, and that each place marker brace corresponds with a data value in the list format() variables that follow.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        "{:5} {:50} by {:50} {:5} {:5}".format(
            album.position,
            album.title,
            album.artist,
            album.numWeeks,
            album.previousPosition
        )
    )

Again, it will probably be clear that the text inside each of the braces such as {:5} tells the format() function how many columns that specific entry will take up.

So {:5} at the beginning of the format string, tells the format() function to use 5 columns for the first variable, and as album.position is the first in the list of variables inside the format() function, the position of the album in the chart will take up the first 5 columns.

The second {} brace contains {:50} which means it will occupy 50 columns, and the second variable is album.title, so the album title will occupy the next 50 columns, and so on...

Notice that in amongst all those {} braces, the format string actually contains the word by, because it’s fine to put other things in the format string alongside the {} braces - even spaces! If it isn’t a {} brace then it just gets produced as is.

Accessing the change information

As mentioned above the album Entry object has a Change object embedded within it.

entry = Entry(
    position = 3,
    previousPosition = 4,
    numWeeks = 26,
    artist = "Sam Smith",
    title = "In The Lonely Hour",
    Change(
        direction = "up",
        amount = 1,
        actual = 1
    )
)

The Change object actually describes the change since last week’s chart in a little bit more detail. It provides access to the following pieces of information about the chart Entry.

  • The amount of change in position since last week’s chart. The is an absolute value - i.e. it describes the amount of change, but not the direction. So unless it is zero, it is always positive.
  • The actual amount of change in positions since last week’s chart. This can be negative, positive or zero.
  • The direction of the change since last week. This is a :py:func`str` and is either up or down.

Printing the change information

So if we wanted to alter our program so that we started printed a summary of whether the album had gone up or down since last week, we could do so as follows.

from pythontop40 import Top40

top40 = Top40()

albums = top40.albums

for album in albums:
    print(
        "{:5} {:50} by {:50} {:5} {:5} - {:4}({:4})".format(
            album.position,
            album.title,
            album.artist,
            album.numWeeks,
            album.previousPosition,
            album.change.direction,
            album.change.amount
        )
    )

You’ll see that we’ve added the following {} braces to the format string

"{:4}({:4})"

and we’ve also added two more variables to the format() function.

album.change.direction,
album.change.amount

These changes result in the following text output when the code is run.

 1 Never Been Better                                  by Olly Murs                                              1     0 - down(   1)
 2 X                                                  by Ed Sheeran                                            23     2 - none(   0)
 3 FOUR                                               by One Direction                                          2     1 - down(   2)
 4 In The Lonely Hour                                 by Sam Smith                                             27     3 - down(   1)
 5 The Endless River                                  by Pink Floyd                                             3     4 - down(   1)
 6 Wanted On Voyage                                   by George Ezra                                           22     8 - up  (   2)
 .
 .
 .
40 The London Sessions                                by Mary J. Blige                                          1     0 - down(  40)

Some finishing touches

Finally, we’ll make some significant changes to the program to add column headings, column formatting, and to alter the text that describes the change since last week.

The output of the new program looks like this.

|   No. | Title                                              | Artist                                             |    Weeks | Previous | Change since last week |
| ----- | -----                                              | ------                                             | -------- | -------- | ---------------------- |
|     1 | Never Been Better                                  | Olly Murs                                          |        1 |        0 |     **NEW ENTRY**      |
|     2 | X                                                  | Ed Sheeran                                         |       23 |        2 |                        |
|     3 | FOUR                                               | One Direction                                      |        2 |        1 |   v by 2 places        |
|     4 | In The Lonely Hour                                 | Sam Smith                                          |       27 |        3 |   v by 1 place         |
|     5 | The Endless River                                  | Pink Floyd                                         |        3 |        4 |   v by 1 place         |
|     6 | Wanted On Voyage                                   | George Ezra                                        |       22 |        8 | ^   by 2 places        |
|     7 | 1989                                               | Taylor Swift                                       |        5 |        7 |                        |
|     8 | Listen                                             | David Guetta                                       |        1 |        0 |     **NEW ENTRY**      |
|     9 | Sonic Highways                                     | Foo Fighters                                       |        3 |        5 |   v by 4 places        |
|    10 | It's The Girls                                     | Bette Midler                                       |        2 |        6 |   v by 4 places        |
|    11 | Partners                                           | Barbra Streisand                                   |       11 |       16 | ^   by 5 places        |
|    12 | Love In Venice                                     | André Rieu                                         |        4 |       11 |   v by 1 place         |
|    13 | Hope                                               | Susan Boyle                                        |        1 |        0 |     **NEW ENTRY**      |
|    14 | Dublin To Detroit                                  | Boyzone                                            |        1 |        0 |     **NEW ENTRY**      |
|    15 | No Sound Without Silence                           | The Script                                         |       11 |       17 | ^   by 2 places        |
|    16 | Forever                                            | Queen                                              |        3 |       13 |   v by 3 places        |
|    17 | Christmas                                          | Michael Bublé                                      |       34 |       27 | ^   by 10 places       |
|    18 | Motion                                             | Calvin Harris                                      |        4 |       18 |                        |
|    19 | Blue Smoke - The Best Of                           | Dolly Parton                                       |       25 |       26 | ^   by 7 places        |
|    20 | Home Sweet Home                                    | Katherine Jenkins                                  |        2 |       10 |   v by 10 places       |
|    21 | The Greatest Hits                                  | Luther Vandross                                    |        2 |       22 | ^   by 1 place         |
|    22 | Strictly Come Dancing                              | Dave Arch & The Strictly Come Dancing Band         |        1 |        0 |     **NEW ENTRY**      |
|    23 | Melody Road                                        | Neil Diamond                                       |        6 |       15 |   v by 8 places        |
|    24 | A Perfect Contradiction                            | Paloma Faith                                       |       38 |       23 |   v by 1 place         |
|    25 | Sirens Of Song                                     | Jools Holland & His Rhythm & Blues Orchestra       |        1 |        0 |     **NEW ENTRY**      |
|    26 | Chapter One                                        | Ella Henderson                                     |        7 |       25 |   v by 1 place         |
|    27 | Serenata                                           | Alfie Boe                                          |        2 |       14 |   v by 13 places       |
|    28 | My Dream Duets                                     | Barry Manilow                                      |        1 |        0 |     **NEW ENTRY**      |
|    29 | Aquostic (Stripped Bare)                           | Status Quo                                         |        6 |       29 |                        |
|    30 | Nothing Has Changed (The Best of David Bowie)      | David Bowie                                        |        2 |        9 |   v by 21 places       |
|    31 | Love In The Future                                 | John Legend                                        |       52 |       32 | ^   by 1 place         |
|    32 | Stand Beside Me: Live In Concert                   | Daniel O'Donnell                                   |        2 |       20 |   v by 12 places       |
|    33 | Royal Blood                                        | Royal Blood                                        |       14 |       35 | ^   by 2 places        |
|    34 | 5 Seconds Of Summer                                | 5 Seconds of Summer                                |       22 |       39 | ^   by 5 places        |
|    35 | Caustic Love                                       | Paolo Nutini                                       |       33 |       38 | ^   by 3 places        |
|    36 | Nostalgia                                          | Annie Lennox                                       |        5 |       30 |   v by 6 places        |
|    37 | No Fixed Address                                   | Nickelback                                         |        2 |       12 |   v by 25 places       |
|    38 | If Everyone Was Listening                          | Michael Ball                                       |        2 |       21 |   v by 17 places       |
|    39 | +                                                  | Ed Sheeran                                         |      168 |       42 | ^   by 3 places        |
|    40 | The London Sessions                                | Mary J. Blige                                      |        1 |        0 |     **NEW ENTRY**      |

And below is the complete program that produced the output above.

from pythontop40 import Top40

top40 = Top40()
format_string = "| {:5} | {:50} | {:50} | {:8} | {:8} | {:22} |"
up_arrow = "^  "
down_arrow = "  v"

# Print the column headings
print(
    format_string.format(
        "  No.",
        "Title",
        "Artist",
        "   Weeks",
        "Previous",
        "Change since last week"
    )
)

# Print the heading underline
print(
    format_string.format(
        "-----",
        "-----",
        "------",
        "--------",
        "--------",
        "----------------------"
    )
)

albums = top40.albums

for album in albums:

    # Create the string that describes that change since last week
    # If the amount of change since last week's chart is 0, or previous position in the chart was 0 (i.e. it is a new
    # entry to the chart), then we should set the change_text to be empty.
    if album.change.amount == 0:
        change_text = ''
    elif album.previousPosition == 0:
        change_text = '    **NEW ENTRY**'
    else:
        # We now know that there was a change in position since last week

        # We want to use the word place if there is only 1 place change, but if there is more than one place change
        # then we want to use the word places. To do this we will use a Python conditional assignment
        places_text = "place" if album.change.amount == 1 else "places"

        # We want to use the up arrow text if the album has moved up since last week, and the down arrow text if it
        # has moved down. To do this we will also use a Python conditional assignment
        arrow_text = up_arrow if album.change.direction == "up" else down_arrow

        # Now let's build the change_text variable from the three components
        # - The arrow text
        # - The amount of change since last week
        # - The place text - using the correct plural term
        change_text = "{} by {} {}".format(
            arrow_text,
            album.change.amount,
            places_text
        )

    # Print the output using the same format string that we used for the heading and underline
    print(
        format_string.format(
            album.position,
            album.title,
            album.artist,
            album.numWeeks,
            album.previousPosition,
            change_text
        )
    )

It might be worth spending a little time looking at the program and the output that it produces, to see if you can see which changes in the code produce which changes in the output.