NBIS

Using someone else's code

Introduction to PythonHT17

Writing functions and modules is a way to structure you code, but it is also naturally a way to avoid repeating the work. Someone else has maybe written code that you could use.

In fact, there are already plenty of modules that come with Python when you install it. Have a look at the Python Module Index.

A small utility

This first part of today’s class is a live code demo. We want to create a small utility that parses a file and print the number of lines, words and/or characters, depending on the given option, such that a typical call would be:

$ mycounter filename --lines

If no option is given, the utility displays all three in a table like:

	Filename: <filename>
	|--- Lines ------ Words ---- Characters --|
	|    12      |     345    |    67890      |
	-------------------------------------------

The resulting code looks like:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys

def _work(filename,search = None):

    lines_counter, words_counter, chars_counter = 0,0,0

    with open(filename,'r',encoding='utf-8') as f:
        
        for line in f:
            
            line = line.strip()
            if line == '' or line.startswith('#'):
                continue

            if search is None or search == '--lines':
                lines_counter += 1

            if search is None or search == '--words':
                words = line.split()
                words_counter += len(words)

            if search is None or search == '--characters':
                chars_counter += len(line) # no leading and trailing whitespaces, no \n

    # Outside the loop, I have now the counters ready.
    # Handling the display:

    if search == '--characters':
        print('{} contains {} characters'.format(filename, chars_counter) )

    if search == '--words':
        print('{} contains {} words'.format(filename, words_counter) )

    if search == '--lines':
        print('{} contains {} lines'.format(filename, lines_counter) )

    if search is None:
        sep = '-' * 64
        print('Filename:',filename.split('/')[-1])
        print(sep)
        print('|{:^20}|{:^20}|{:^20}|'.format('Lines','Words','Characters'))
        print(sep)
        print('|{:^20}|{:^20}|{:^20}|'.format(lines_counter,words_counter,chars_counter))
        print(sep)




def usage():
    print('''\
Usage: code.py <filename> [options]

Options are:
--------
--lines         displays the number of lines only
--words         displays the number of words only
--characters    displays the number of characters only

If no option is given, it displays all in a table like:

     
     Filename: <filename>
     -------------------------------------------
     |   Lines    |    Words   |  Characters   |
     -------------------------------------------
     |    12      |     345    |    67890      |
     -------------------------------------------

''')


if __name__ == '__main__':

    filename = None
    option = None
    
    if len(sys.argv) >= 2:
        filename = sys.argv[1]

    if len(sys.argv) >= 3:
        option = sys.argv[2]

    if not filename or option not in ('--lines','--words','--characters',None):
        usage()
    else:        
        _work(filename, search = option) # option might be None

We now put that program in our PATH and rename it to mycounter.

Note the string formatting for the table. This is what we now cover. Head to the Format String Syntax webpage.

Importing math

The math module provides mathematical functions. For example, in order to use the cosinus and sinus functions, you can import math, which contains their implementation.

Given 2 points on a flat surface, with coordinates (x1,y1) and (x2,y2), define a function that calculates the distance between the 2 points.

Secondly, define a function that calculates the angle between the X axis and the segment formed by the 2 points.

Finally, write a small test for the above functions.

House Database

For the second example of code written by someone else (yes, yours truly :) ), we are given a database containing information about Real Estate around Uppsala.

The first step is to download the required files. We must then read the documentation of that code. We don’t know the internals, but there are surely methods and new defined types (called class, but not part of this course) that we can use for this assignment. The documentation is at the end of this page.

What is the price of the cheapest house around a given center and within 2 kilometers? The center is given as both:

You can plot the results to an HTML file, which internally uses Google Maps (so we’ll have to fetch an API key first!). If you do not have a Google account, or don’t want to bother with the API key, you can always content yourself with the print function.

Another critieria

Change the criteria now, and find the most expensive house per square meters.

An example of map

Module documentation

The code to produce would typically start by importing the necessary functions and classes from the db module.

from db import HomeDB

# Initilizing the database
database = HomeDB('uppsala.sqlite')
database.connect()
selection = database.select('rooms > 1 and rooms < 3 and area > 58 and rent < 3000')
database.disconnect()

# Looping now through the selection
for home in selection:
	# Do something

# Then plot

The above code essentially initializes the database, fetches some selection, based on some criteria, and plots the result on a map. Yes, it’s visual, it’ fun.

You could use this template file that you have to complete.


You can of course look at the documentation from the sourcecode itself, using the help function.

$ python
>>> import db
>>> help(db.plot)

The plot function has a few keyword parameters and two first positional arguments for the selection and Google API key.

plot(selection,
     google_key,
     output='selection.html',
     special=None,
     zoom=12,
     latitude=59.83732598851705,
     longitude=17.64549846959149,
     radius=5000)
	

    Outputs the selection to map file. Section is a list of HomeEntry.
    The center is given by latitude and longitude and marked on the map with a blue marker.
    The map draws a radius around the center.
    When the special is used, an extra green marker is ploted in the map.

    Note: this requires you to pass a Google Maps API key.
    You can fetch one here: https://developers.google.com/maps/documentation/javascript/get-api-key

For each home, it is possible to call the following functions:

home.get_location()
returns a pair (latitude,longitude) as a float tuple.
home.get_price()
returns the price that home was sold for, in sek.
home.get_area()
returns its surface in m2.

The haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes. This will be useful for filtering the selection using the radius.

$ python
>>> import db
>>> help(db.haversine)

Help on function haversine in module db:

haversine(lat1, lon1, lat2, lon2)
    Calculate the great circle distance (in m) between two points
    on the earth (specified in decimal degrees)