6/25/2012

Python on the Raspberry Pi: Leap Year Module

Python on the Raspberry Pi: Leap Year Module

I mentioned in the first Python on the Raspberry Pi: Numbers and Dates that I if I had an advanced student, I might have created a function for the pseudo code that was posted at Wikipedia about leap years. This post describes settings that are appropriate for vim for use as an editor for Python, the algorithm used for calculating a leap year, and how to use a module called leapyear, stored in a file called leapyear.py. This file is available for download at https://github.com/wrightrocket/PythonOnTheRaspberryPi.

Vim Settings for Python  

I like to use vim to edit in a terminal, but I create a ~/.vimrc file to add some settings that make it work well for Python. Of course, you can use an editor you like, but be careful not to mix tabs and spaces for creating your indentation.

These settings for vim set the width of a tab to four spaces, but convert any new tabs to spaces automatically.

My ~/.vimrc:
set expandtab  
set tabstop=4 
set list       
set backup 

Leap Year Algorithm

The Algorithm came from a WikiPedia article found at http://en.wikipedia.org/wiki/Leap_year. This is implemented in the leapyear.py file as the function named isLeapYear(). 

if year modulo 400 is 0 then 
   is_leap_year
else if year modulo 100 is 0 then 
   not_leap_year
else if year modulo 4 is 0 then 
   is_leap_year
else
   not_leap_year



In the directory where you start Python (or on the PYTHONPATH) create a file named leapyear.py containing the following code:

#!/usr/bin/python
''' Module provides a simple function to test whether a year is a leap year'''

def isLeapYear(year):
    ''' Returns True if a year is a leap year, otherwise False 

    This function first checks the passed value of year to ensure to that is an Integer.
    It then tests the year to see if it is a Leap Year according to the algorithm 
    expressed in the pseudo-code found at http://en.wikipedia.org/wiki/Leap_year '''

    if type(year) is not int:  #  verifying year is an int (Integer) object
        # if year is not an Integer then return False
        # print year, "is a not an Integer, so unable to check if it a leap year"
        return False
    else:  # This is where the pseudo-code of the function begins
        if not (year % 400):  
            # zero is considered False
            return True 
            #  if year / 400.0 has zero remainder then is a leap year
        elif not (year % 100):
            return False  
            # if year / 100.0 has zero remainder than is not leap year
        elif not (year % 4):
            return True  
            # if year / 4 has zero remainder and both above are not true
        else:
            return False  # otherwise it is not a leap year

def test_isLeapYear():
    ''' Display a range of leap years from 1752 to 2012 '''
    print "\nPrinting the the list of leap years from 1752 to 2012:"
    years = 0
    for year in range(1752, 2013):
        # start the year at 1752, when they began and go to 2013 - 1
        if isLeapYear(year):
            print year, 
            # don't add a new line by using trailing comma
            years += 1
            # add one to the years variable
            if not (years % 10):
                # every 10 leap years print a new line
                print 
        else:
            pass
            # do nothing instead of printing years that are not leap years
            # print "%s IS NOT a leap year" % year
    else:
        # when through with the loop finish the line of print
        print


def test_logic_isLeapYear():
    ''' This function tests every branch of isLeapYear() to verify logic 

    If this function raises an AssertionError then the logic of isLeapYear() 
    is not working correctly according to pseudo-code for calculating 
    Leap Years found at http://en.wikipedia.org/wiki/Leap_year'''

    print "\nTesting each branch of isLeapYear for accurate results:"
    print "Is the year 2000 a leap year? Why, or why not?", isLeapYear(2000)
    # 2000 modulus 400 is 0, so it is a leap year
    assert isLeapYear(2000)
    # if isLeapYear returns False, then this assertion will halt execution
    print "Is the year 1800 a year year? Why, or why not?", isLeapYear(1800)
    # 1800 modulus 400 != 0, and 1800 % 100 == 0, so it is not a leap year
    assert not isLeapYear(1800)
    # if isLeapYear returns True, then this assertion will halt execution
    print "Is the year 2012 a leap year? Why, or why not?", isLeapYear(2012)
    # 2012 % 400 != 0, 2012 % 100 != 0, but 2012 % 4 == 0, so it is a leap year
    assert isLeapYear(2012)
    # if isLeapYear returns False, then this assertion will halt execution
    print "Is the year 2013 a leap year? Why, or why not?", isLeapYear(2013)
    # 2013 % 400 != 0, 2013 % 100 != 0, and 2013 % 4 != 0, so not a leap year
    assert not isLeapYear(2013)
    # if isLeapYear returns True, then this assertion will halt execution

if __name__ == '__main__':
    # when not being imported execute the following block of code
    test_isLeapYear()
    test_logic_isLeapYear()

Execute a Module from the command line:

When the module is executed directly, and not being imported, then it will execute the code indented underneath if __name__ == '__main__':


pi@raspberrypi:~/PlayingWithPython$ python leapyear.py 

Printing the the list of leap years from 1752 to 2012:
1752 1756 1760 1764 1768 1772 1776 1780 1784 1788
1792 1796 1804 1808 1812 1816 1820 1824 1828 1832
1836 1840 1844 1848 1852 1856 1860 1864 1868 1872
1876 1880 1884 1888 1892 1896 1904 1908 1912 1916
1920 1924 1928 1932 1936 1940 1944 1948 1952 1956
1960 1964 1968 1972 1976 1980 1984 1988 1992 1996
2000 2004 2008 2012

Testing each branch of isLeapYear for accurate results:
Is the year 2000 a leap year? Why, or why not? True
Is the year 1800 a year year? Why, or why not? False
Is the year 2012 a leap year? Why, or why not? True
Is the year 2013 a leap year? Why, or why not? False
pi@raspberrypi:~/PlayingWithPython$ chmod a+x leapyear.py
pi@raspberrypi:~/PlayingWithPython$ ./leapyear.py

Printing the the list of leap years from 1752 to 2012:
1752 1756 1760 1764 1768 1772 1776 1780 1784 1788
1792 1796 1804 1808 1812 1816 1820 1824 1828 1832
1836 1840 1844 1848 1852 1856 1860 1864 1868 1872
1876 1880 1884 1888 1892 1896 1904 1908 1912 1916
1920 1924 1928 1932 1936 1940 1944 1948 1952 1956
1960 1964 1968 1972 1976 1980 1984 1988 1992 1996
2000 2004 2008 2012

Testing each branch of isLeapYear for accurate results:
Is the year 2000 a leap year? Why, or why not? True
Is the year 1800 a year year? Why, or why not? False
Is the year 2012 a leap year? Why, or why not? True
Is the year 2013 a leap year? Why, or why not? False

Import the Module into the interpreter:

The way a module is used from other modules, or in the interpreter is to use an import statement. In the case of importing a module, the code indented underneath if __name__ == '__main__': is not run. 

First the whole module is imported into the main namespace. Accessing functions of the module will require modulename.functionname. 

Next, a function is directly imported into the namespace. The function can be accessed directly like Python's own built in functions.

The use of help(module), help(module.function) and help(function) demonstrate how to view the '''docstrings''' used when creating a module or function. 

The use of dir() illustrates how to view the main namespace. The use of dir(module) how to view the namespace of a module. 
pi@raspberrypi:~/PlayingWithPython$ python
Python 2.7.3 (default, Jun 18 2012, 16:19:55) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.


>>> import leapyear
>>> leapyear.isLeapYear(2012)
True
>>> dir(leapyear)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'isLeapYear', 'test_isLeapYear', 'test_logic_isLeapYear']
>>> help(leapyear)
>>> help(leapyear.isLeapYear)

>>> leapyear.test_isLeapYear()

Printing the the list of leap years from 1752 to 2012:
1752 1756 1760 1764 1768 1772 1776 1780 1784 1788
1792 1796 1804 1808 1812 1816 1820 1824 1828 1832
1836 1840 1844 1848 1852 1856 1860 1864 1868 1872
1876 1880 1884 1888 1892 1896 1904 1908 1912 1916
1920 1924 1928 1932 1936 1940 1944 1948 1952 1956
1960 1964 1968 1972 1976 1980 1984 1988 1992 1996
2000 2004 2008 2012
>>> leapyear.test_logic_isLeapYear()

Testing each branch of isLeapYear for accurate results:
Is the year 2000 a leap year? Why, or why not? True
Is the year 1800 a year year? Why, or why not? False
Is the year 2012 a leap year? Why, or why not? True
Is the year 2013 a leap year? Why, or why not? False
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'leapyear', 'sys']
>>> from leapyear import isLeapYear
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'isLeapYear', 'leapyear', 'sys']
>>> help(isLeapYear)
>>> isLeapYear(2000)
True
>>> isLeapYear(1800)
False
>>> import this

By creating a file called leapyear.py, we created a module. By using python on the command line, we were able to execute this module in the __main__ namespace. By using an import modulename statement, we were able to import name of the module into our  __main__ namespace. We were then able to reference the functions by using modulename.functionname. By using a from modulename import functioname, we were then able to access the functioname directly, or in or __main__ namespace. Finally, by using 'import this' we have uncovered a Python 2 Easter Egg.





No comments:

About Me - WrightRocket

My photo

I've worked with computers for over 30 years, programming, administering, using and building them from scratch.

I'm an instructor for technical computer courses, an editor and developer of training manuals, and an Android developer.