What we will cover in this part

  • More detail on data types
  • For Loop Statement
  • Exercise: More features for the Website monitoring tool

Solution to exercise in part 1

Before we get started, here is a possible solution to the exercise from part 1

#! python

import requests
from time import time

def get_website(url):
    return requests.get(url)

if __name__ == '__main__':
    start_time = time()
    response = get_website('http://aboutsimon.com/')
    end_time = time()
    elapsed_time = end_time - start_time

    if response.status_code == 200:
        print 'HTTP Status Code: OK'
    else:
        print 'Error: Unexpected HTTP Status Code %d' % (response.status_code)

    if elapsed_time < 0.5:
        print 'GET request took: %f seconds' % (elapsed_time)
    else:
        print 'Error: Request took longer than 0.5 seconds.'

    if len(response.content) > 0:
        print 'Page size: %d byte' % (len(response.content))
    else:
        print 'Error: Page size too small.'

More detail on data types

Python has a couple of built-in data types. Each built-in data type can be constructed by dedicated expressions like a = 1, which will construct an integer object or b = "mystring", which will construct an string object. Built-in data types can also be constructed by a constructor function. Constructor functions are basically used to convert/cast data types.

Numeric Types

There are four distinct numeric types. Integer, long integer, floating point number and complex number. Each of them differ in what values they can represent. The maximum and minimum value depends on the OS architecture you are running on.

All numeric types can be used with the following operators. (There are more if you’re interested look into Built-in Types.

Operators

  • x + y: Add y and x
  • x - y: Subtract y from x
  • x * y: Multiply x and y
  • x / y: Divide x and y
  • x // y: Divide x and y, floor (round down) the qoutient
  • x % y: Modulo, find the remainder of the division x / y
  • x ** y: x to the power y

Augmented Assignment (Operator Shortcut)

For all operators mentioned above exist a shortcut to execute an operator combined with an assignment to a variable. For example if you like to increase a counter by one for each step you may write counter = counter + 1, but you could also write counter += 1, which is exactly the same. Unlike many other languages, Python does not implement ++ nor --, for increasing or decreasing a variable by one.

Integer

  • Type: int
  • Constructor function: int(x, base=10)
  • Syntax: a = 1
a = 1
a += 2  # Result: 3
a = a / 2 # Result: 1
a = int('1') # Cast/Convert a string object to an integer object, with base 10 (default base)
a = int('0xff', 16)  # Cast/Convert a string object holding a hex number, with base 16 (base hexadecimal)
a = int(0xff)  # Cast/Convert a hex value to int. We don't need a base, because the hex number already is an integer, only in hex notation.

Line 3 is special, because dividing 3 / 2 does not result in 1.5, but in 1. This is because we divide two integers, which will result in an integer and an integer can’t represent a number like 1.5.

Long Integer

  • Type: long
  • Constructor function: long(x, base=10)
  • Syntax: a = 123718923789173891723891723897

The main difference between int and long is, long can represent very large numbers (unlimited precision) and int, is limited to a max and min value. If max of an integer is reached, it will be automatically converted to a long. Don’t worry about integer overflow.

Floating Point Number

  • Type: float
  • Constructor function: float(x)
  • Syntax: a = 1.0123
a = 1.0
a += 2.0  # Result: 3.0
a = a / 2 # Result: 1.5
a = float('1.0') # Cast/Convert a string object to an float object
a = int(1)  # Cast/Convert a int to float. Result: 1.0

Complex Number

  • Type: complex
  • Constructor function: complex([real[, imag]])
  • Syntax: a = 4j

Sequence Types

A sequence type is an ordered list of objects. There are 7 sequence types in Python string, unicode, list, tuple, bytearray, buffer, and xrange. I will cover string, unicode, list and tuple for now. A sequence type can be iterated, meaning you can access each object inside a sequence via loops. Objects inside a sequence can be accessed directly with an index or a slice, a range of indices. The index of a sequence always starts at 0. So, the first item of a sequence is index 0. The index of a sequence can be negative. Negative indices will get objects from the end of a sequence. For example s[-1] will get the last item of a sequence. Index ranges are always exclusive of the range end. The range 0 to 5, will get the items 0, 1, 2, 3 and 4.

Sequence Operations

  • s[i]: Get an object in a sequence by index n
  • s[x:y]: Get all objects in a sequence by a range index x to y
  • s[x:y:k]: Get all objects in a sequence by a range index x to y, with k steps
  • x in s: Check if object x is in sequence s
  • x not in s: Check if object x is not in sequence s
  • len(s): Get the length of a sequence
  • min(s): Get the smallest object of a sequence
  • max(s): Get the largest object of a sequence
  • s.index(n): Get the index of the first occurence of object n in sequence s
  • s.count(n): Get the count of occurences of object n in sequence s
  • s1 + s2: Concatenate sequence s1 and sequence s2 to build one sequence out of s1 and s2

String

  • Type: str
  • Constructor function: str(object=’’)
  • Syntax: a = 'my string value' or a = "my string value"

A str object is by default an ASCII encoded string. Each byte represents one character. Because a string is a sequence type, it can be accessed like a list. You can iterate over a string object, to get each byte (with ASCII each character too), or get a sub string by accessing the string with an index range. The max() and min() functions will return the largest or smallest character measured by its numeric value. See ASCII Table.

s = "I like movies."
print s[0]  # Result: "I"
print s[-7:-1]  # Result: "movies"
print len(s)  # Result: 14
print max(s)  # Result: "v" (Because the character "v" inside the string has the largest numeric value.)
print s[0:-1] + " very much."  # Result: "I like movies very much."

# This will print each character of the string object
for character in s:
    print character

print s.count('i')  # Result: 2

print s[::2]  # Print every second character of the string object

The last line is strange. There are no values, which specify the start and end of the index range?! Omitting the values of a index ranges means, start at the beginning and stop at the end of the sequence. s[:2] is equal to s[0:2], from beginning of the sequence to index 2. s[5:] is equal to s[5:len(s)], from index 5 to the end of the sequence. s[:] is equal to s[0:len(s)], from the beginning of the sequence to the end of the sequence. By the way, there is no difference in creating a string instance with ‘single quotes’ or “double quotes”, in some languages like Perl or Go there is a difference, in Python is none.

Unicode

  • Type: unicode
  • Constructor function: unicode(object[, encoding[, errors]])
  • Syntax: a = u'my ?' or a = u"my ?"

str objects and unicode objects are pretty much the same, except that str is a byte representation of a string. If you construct a str with a multi byte unicode letter for example the Chines sign “?” you will end up with a sequence of length 3, because the sign “?” uses 3 bytes. If you print something like print '?'[0] it does not print “?”, but the first byte 0xE4. A unicode object is aware of string codec and printing u'?'[0] will result in “?”.

a = u'?'
print len(a)  # Result: 1

List

  • Type: list
  • Constructor function: list([iterable])
  • Syntax: a = [1, 2, "hi"]

A list is a container which can hold a ordered list of all kind of objects. You can add or remove items to or from a list. A list can be extended by an other list or sorted by its items.

l = [1, 2, 3]  # New list
print len(l)  # Result: 3

l.append("hi!")  # add an item to the list
print len(l)  # Result: 4

value = l.pop()  # Remove and return the last item from the list
print value  # Result: "hi!"
print len(l)  # Result: 3

l.extend([4, 5, 6])  # Extend the list by an other list
print len(l)  # Result: 6

l.reverse()  # Reverse the list
print l  # Result: [6, 5, 4, 3, 2, 1]

l.sort()  # Sort the list
print l  # Result: [1, 2, 3, 4, 5, 6]

print l[:4]  # Result: [1, 2, 3, 4]

You can find more on lists and its usage beside a plain data storage in the Data Structures Tutorial on python.org.

Tuple

  • Type: tuple
  • Constructor function: tuple([iterable])
  • Syntax: a = (1, 2, "hi")

A tuple object is a kind of lightweight list, except you can’t modify a tuple. You can’t add or remove items, nor change the order of a tuple. See it like a static list. More on tuples an other time.

Mapping Types

The last new data type for today is the one and only mapping type in Python the dictionary dict.

dict

  • Type: dict
  • Constructor function: dict(**kwarg) or dict(mapping, **kwarg) or dict(iterable, **kwarg)
  • Syntax: a = {"numbers": [1,2,3,4], "strings": ["hello", "you"]}

The dict is a really powerful data type. Inside a dict you can map all kind of objects to other objects. It has great use for a wide range of purposes. A dict entry consists of a key:value mapping. The entries of a dict are not ordered, meaning if you initialize a dict in a specific order, it is not guaranteed it will stay in this order.

mydict = {}  # New empty dict
mydict['name'] = 'Simon'  # A new dict entry, mapping an string object to a string object
mydict['age'] = 28  # Mapping a string object to an int object
mydict['languages'] = ['Python', 'Perl', 'Go', 'JavaScript']  # Mapping a string object to a list object

print mydict.keys()  # Get all key objects from the dict an print them. Result: ['languages', 'age', 'name']

print mydict['name']  # Get the value of the field mapped to 'name' and print it. Result: 'Simon'

mydict['firstname'] = 'Simon'
del mydict['name']  # Delete the 'name' mapping from the dict.

print len(mydict)  # Return the number of items in the dict. Result: 3

# check if the key 'name' is in mydict. Result: False
print 'name' in mydict

print mydict.values()  # Get all values from mydict and print them. Result: [['Python', 'Perl', 'Go', 'JavaScript'], 28, 'Simon']

mydict.clear()  # Delete all items from mydict
print len(mydict)  # Result: 0

mydict['Simon'] = {'age': 28, 'languages': ['Python', 'Perl', 'Go', 'JavaScript']}
print ['Simon']['age']  # get the value mapped to 'Simon', get the value mapped to 'age' inside the 'Simon' value and print it

You may noticed the del statement. With del you can delete stuff. Delete items from a list, delete items from a dict, even delete entire variables. You can find more on dicts here.

For Loop

This was boring, but necessary, sorry. Now the last new thing for today, the for loop. With for you can iterate sequences. The for keyword is followed by a variable name, where the current object for the iteration is assigned to, followed by the in keyword, followed by the sequence.

# iterate the list, each item will be assigned to item
for item in [1, 2, 3]:
    print item  # print the current value of item

As long as an object provides a sequence to iterate you can use the for loop. In the next example we will iterate a dict.

mydict = {}
mydict['Simon'] = {'age': 28, 'languages': ['Python', 'Perl', 'Go', 'JavaScript']}

# iterate the keys list, each item will be assigned to item
for dict_key in mydict.keys():
    print dict_key  # Result: 'Simon'
    print mydict[dict_key]  # Result: {'age': 28, 'languages': ['Python', 'Perl', 'Go', 'JavaScript']}

You can nest more loops, to iterate the hole dict, with all keys/subkeys and values/subvalues.

mydict = {}
mydict['Simon'] = {'age': 28, 'languages': ['Python', 'Perl', 'Go', 'JavaScript']}

# iterate the keys list, each item will be assigned to item
for dict_key in mydict.keys():
    print dict_key

    for child_dict_key in mydict[dict_key].keys():
        print child_dict_key
        print mydict[dict_key][child_dict_key]

Exercise

Now, extent your Website monitoring tool. Instead of just checking one Website, we will check more and instead of checking static return codes and response time, we will make it configurable for each Website. Create a dict. Add an entry for each Website you like to check. The key of the dict should be a name for the Website. Mapped to this name is an other dict with key:value pairs named ‘url’, ‘status_code’ and ‘response_time’. Iterate over the dict, GET each URL and check if the response.status_code is equal to the configured status_code and the elapsed_time less or equal to the configured response_time. Modify your print statements, to print also the name of the Website, which is currently checked. The dict is a global variable, so it’s defined before the if __name__ == '__main__':.

Little help:

websites = {
    "Simon's Blog": {
        'url': 'http://aboutsimon.com/',
        'status_code': 200,
        'response_time': 0.5,
    }
}

Add as much Websites to the dict, as you like.