MacLochlainns Weblog

Michael McLaughlin's Technical Blog

Site Admin

Archive for the ‘Python Developer’ tag

Read list of a dictionaries

without comments

My students wanted a quick example of how to read a list of a dictionaries in Python. So, here it is:

#!/usr/bin/python
 
# Declare list of dictionaries.
cakes = [{'cake':"vanilla",'frosting':"chocolate"}
        ,{'cake':"chocolate",'frosting':"vanilla"}]
 
# Read the list of dictionaries.
for lkey, lvalue in enumerate(cakes):
  print lvalue['cake'] + " with " + lvalue['frosting'] + " frosting."

Naturally, a list can contain many things and you should ensure each value you read is a dictionary before trying to read it as a dictionary. At least, I’d suggest you check.

Hope this answers the how.

Written by maclochlainn

June 1st, 2017 at 9:09 pm

Install PyGame on Fedora

without comments

The PyGame library is a wonderful tool for building games with Python. It lets you accomplish a great deal by simply managing events. You need to understand how to use Python functions, modules, and events to build games with this Python library.

You can download and install the PyGame library with the yum utility like this:

yum install -y pygame

It should generate the following list when you install it as the root user:

Loaded plugins: langpacks, refresh-packagekit
Available Packages
pygame.x86_64                        1.9.1-14.fc20                        fedora
[root@localhost ~]# yum install -y pygame
Loaded plugins: langpacks, refresh-packagekit
Resolving Dependencies
--> Running transaction check
---> Package pygame.x86_64 0:1.9.1-14.fc20 will be installed
--> Processing Dependency: numpy for package: pygame-1.9.1-14.fc20.x86_64
--> Processing Dependency: libportmidi.so.0()(64bit) for package: pygame-1.9.1-14.fc20.x86_64
--> Processing Dependency: libSDL_ttf-2.0.so.0()(64bit) for package: pygame-1.9.1-14.fc20.x86_64
--> Processing Dependency: libSDL_mixer-1.2.so.0()(64bit) for package: pygame-1.9.1-14.fc20.x86_64
--> Processing Dependency: libSDL_image-1.2.so.0()(64bit) for package: pygame-1.9.1-14.fc20.x86_64
--> Running transaction check
---> Package SDL_image.x86_64 0:1.2.12-7.fc20 will be installed
---> Package SDL_mixer.x86_64 0:1.2.12-5.fc20 will be installed
--> Processing Dependency: libmikmod for package: SDL_mixer-1.2.12-5.fc20.x86_64
---> Package SDL_ttf.x86_64 0:2.0.11-4.fc20 will be installed
---> Package numpy.x86_64 1:1.8.2-2.fc20 will be installed
--> Processing Dependency: python-nose for package: 1:numpy-1.8.2-2.fc20.x86_64
---> Package portmidi.x86_64 0:217-9.fc20 will be installed
--> Running transaction check
---> Package libmikmod.x86_64 0:3.3.6-3.fc20 will be installed
---> Package python-nose.noarch 0:1.3.0-1.fc20 will be installed
--> Finished Dependency Resolution
 
Dependencies Resolved
 
================================================================================
 Package            Arch          Version                  Repository      Size
================================================================================
Installing:
 pygame             x86_64        1.9.1-14.fc20            fedora         2.1 M
Installing for dependencies:
 SDL_image          x86_64        1.2.12-7.fc20            fedora          41 k
 SDL_mixer          x86_64        1.2.12-5.fc20            fedora          91 k
 SDL_ttf            x86_64        2.0.11-4.fc20            fedora          22 k
 libmikmod          x86_64        3.3.6-3.fc20             updates        142 k
 numpy              x86_64        1:1.8.2-2.fc20           updates        3.0 M
 portmidi           x86_64        217-9.fc20               fedora          26 k
 python-nose        noarch        1.3.0-1.fc20             fedora         272 k
 
Transaction Summary
================================================================================
Install  1 Package (+7 Dependent packages)
 
Total download size: 5.7 M
Installed size: 21 M
Downloading packages:
(1/8): SDL_image-1.2.12-7.fc20.x86_64.rpm                   |  41 kB  00:00     
(2/8): SDL_mixer-1.2.12-5.fc20.x86_64.rpm                   |  91 kB  00:00     
(3/8): portmidi-217-9.fc20.x86_64.rpm                       |  26 kB  00:00     
(4/8): SDL_ttf-2.0.11-4.fc20.x86_64.rpm                     |  22 kB  00:00     
(5/8): libmikmod-3.3.6-3.fc20.x86_64.rpm                    | 142 kB  00:00     
(6/8): numpy-1.8.2-2.fc20.x86_64.rpm                        | 3.0 MB  00:02     
(7/8): pygame-1.9.1-14.fc20.x86_64.rpm                      | 2.1 MB  00:01     
(8/8): python-nose-1.3.0-1.fc20.noarch.rpm                  | 272 kB  00:00     
--------------------------------------------------------------------------------
Total                                              1.7 MB/s | 5.7 MB  00:03     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Installing : SDL_ttf-2.0.11-4.fc20.x86_64                                 1/8 
  Installing : SDL_image-1.2.12-7.fc20.x86_64                               2/8 
  Installing : portmidi-217-9.fc20.x86_64                                   3/8 
  Installing : libmikmod-3.3.6-3.fc20.x86_64                                4/8 
  Installing : SDL_mixer-1.2.12-5.fc20.x86_64                               5/8 
  Installing : python-nose-1.3.0-1.fc20.noarch                              6/8 
  Installing : 1:numpy-1.8.2-2.fc20.x86_64                                  7/8 
  Installing : pygame-1.9.1-14.fc20.x86_64                                  8/8 
  Verifying  : pygame-1.9.1-14.fc20.x86_64                                  1/8 
  Verifying  : SDL_mixer-1.2.12-5.fc20.x86_64                               2/8 
  Verifying  : python-nose-1.3.0-1.fc20.noarch                              3/8 
  Verifying  : libmikmod-3.3.6-3.fc20.x86_64                                4/8 
  Verifying  : 1:numpy-1.8.2-2.fc20.x86_64                                  5/8 
  Verifying  : portmidi-217-9.fc20.x86_64                                   6/8 
  Verifying  : SDL_image-1.2.12-7.fc20.x86_64                               7/8 
  Verifying  : SDL_ttf-2.0.11-4.fc20.x86_64                                 8/8 
 
Installed:
  pygame.x86_64 0:1.9.1-14.fc20                                                 
 
Dependency Installed:
  SDL_image.x86_64 0:1.2.12-7.fc20        SDL_mixer.x86_64 0:1.2.12-5.fc20      
  SDL_ttf.x86_64 0:2.0.11-4.fc20          libmikmod.x86_64 0:3.3.6-3.fc20       
  numpy.x86_64 1:1.8.2-2.fc20             portmidi.x86_64 0:217-9.fc20          
  python-nose.noarch 0:1.3.0-1.fc20      
 
Complete!

I hope this helps folks install the software.

Written by maclochlainn

March 25th, 2017 at 1:49 am

Install EasyGUI on Fedora

without comments

The EasyGUI library is a nice tool for developing GUI applications. It doesn’t require you to know event-driven programming to write basic GUI applications because it’s based on Python functions.

You can download and install the EasyGUI library with yum utility like this:

yum install -y python-easygui

It should generate the following list:

Loaded plugins: langpacks, refresh-packagekit
mysql-connectors-community                                  | 2.5 kB  00:00     
mysql-tools-community                                       | 2.5 kB  00:00     
mysql56-community                                           | 2.5 kB  00:00     
pgdg93                                                      | 3.6 kB  00:00     
updates/20/x86_64/metalink                                  | 2.8 kB  00:00     
Resolving Dependencies
--> Running transaction check
---> Package python-easygui.noarch 0:0.96-7.fc20 will be installed
--> Processing Dependency: tkinter for package: python-easygui-0.96-7.fc20.noarch
--> Processing Dependency: python-setuptools for package: python-easygui-0.96-7.fc20.noarch
--> Running transaction check
---> Package python-setuptools.noarch 0:1.4.2-1.fc20 will be installed
---> Package tkinter.x86_64 0:2.7.5-16.fc20 will be installed
--> Processing Dependency: libtk8.5.so()(64bit) for package: tkinter-2.7.5-16.fc20.x86_64
--> Processing Dependency: libtcl8.5.so()(64bit) for package: tkinter-2.7.5-16.fc20.x86_64
--> Processing Dependency: libTix.so()(64bit) for package: tkinter-2.7.5-16.fc20.x86_64
--> Running transaction check
---> Package tcl.x86_64 1:8.5.14-1.fc20 will be installed
---> Package tix.x86_64 1:8.4.3-11.fc20 will be installed
---> Package tk.x86_64 1:8.5.14-1.fc20 will be installed
--> Finished Dependency Resolution
 
Dependencies Resolved
 
================================================================================
 Package                 Arch         Version               Repository     Size
================================================================================
Installing:
 python-easygui          noarch       0.96-7.fc20           fedora        481 k
Installing for dependencies:
 python-setuptools       noarch       1.4.2-1.fc20          updates       413 k
 tcl                     x86_64       1:8.5.14-1.fc20       fedora        1.9 M
 tix                     x86_64       1:8.4.3-11.fc20       fedora        253 k
 tk                      x86_64       1:8.5.14-1.fc20       fedora        1.4 M
 tkinter                 x86_64       2.7.5-16.fc20         updates       316 k
 
Transaction Summary
================================================================================
Install  1 Package (+5 Dependent packages)
 
Total download size: 4.7 M
Installed size: 13 M
Downloading packages:
(1/6): python-setuptools-1.4.2-1.fc20.noarch.rpm            | 413 kB  00:00     
(2/6): python-easygui-0.96-7.fc20.noarch.rpm                | 481 kB  00:00     
(3/6): tkinter-2.7.5-16.fc20.x86_64.rpm                     | 316 kB  00:00     
(4/6): tix-8.4.3-11.fc20.x86_64.rpm                         | 253 kB  00:01     
(5/6): tcl-8.5.14-1.fc20.x86_64.rpm                         | 1.9 MB  00:01     
(6/6): tk-8.5.14-1.fc20.x86_64.rpm                          | 1.4 MB  00:03     
--------------------------------------------------------------------------------
Total                                              1.5 MB/s | 4.7 MB  00:03     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction (shutdown inhibited)
  Installing : 1:tcl-8.5.14-1.fc20.x86_64                                   1/6 
  Installing : 1:tk-8.5.14-1.fc20.x86_64                                    2/6 
  Installing : 1:tix-8.4.3-11.fc20.x86_64                                   3/6 
  Installing : tkinter-2.7.5-16.fc20.x86_64                                 4/6 
  Installing : python-setuptools-1.4.2-1.fc20.noarch                        5/6 
  Installing : python-easygui-0.96-7.fc20.noarch                            6/6 
  Verifying  : 1:tk-8.5.14-1.fc20.x86_64                                    1/6 
  Verifying  : tkinter-2.7.5-16.fc20.x86_64                                 2/6 
  Verifying  : 1:tix-8.4.3-11.fc20.x86_64                                   3/6 
  Verifying  : 1:tcl-8.5.14-1.fc20.x86_64                                   4/6 
  Verifying  : python-easygui-0.96-7.fc20.noarch                            5/6 
  Verifying  : python-setuptools-1.4.2-1.fc20.noarch                        6/6 
 
Installed:
  python-easygui.noarch 0:0.96-7.fc20                                           
 
Dependency Installed:
  python-setuptools.noarch 0:1.4.2-1.fc20       tcl.x86_64 1:8.5.14-1.fc20      
  tix.x86_64 1:8.4.3-11.fc20                    tk.x86_64 1:8.5.14-1.fc20       
  tkinter.x86_64 0:2.7.5-16.fc20               
 
Complete!

You can then test the EasyGUI library with the following three lines of code inside the IDLE interpreter:

Python 2.7.5 (default, Apr 10 2015, 08:09:05) 
[GCC 4.8.3 20140911 (Red Hat 4.8.3-7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import easygui
>>> flavor = easygui.enterbox("What flavor of bum do you like?")
>>> if easygui.msgbox("You like " + flavor + " gum.") == 'OK':
...   print "OK button clicked ..."
... 
OK button clicked ...

The easygui.enterbox call displays the image below. Enter “Peppermint” in the entry box and click the OK button to assign the “Peppermint” string literal to the flavor variable.

The easygui.msgbox call displays the message below:

When you click the OK button, the program returns an “OK” string to the Python code. It prints the string “OK button clicked …” string:

As always, I hope this helps those looking for instructions and a quick way to play with Python and GUI applications.

Written by maclochlainn

February 22nd, 2017 at 1:35 am

Create a Python Module

without comments

Sometime formal programming documentation is less than clear. At least, it’s less than clear until you’ve written your first solution. The Modules section of the Python language is one of those that takes a few moments to digest.

Chapters 22 and 23 in Learning Python gives some additional details but not a clear step-by-step approach to implementing Python modules. This post is designed to present the steps to write, import, and call a Python module. I figured that it would be helpful to write one for my students, and posting it in the blog seemed like the best idea.

I wrote the module to parse an Oracle version string into what we’d commonly expect to see, like the release number, an “R”, a release version, and then the full version number. The module name is more or less equivalent to a package name, and the file name is effectively the module name. The file name is strVersionOracle.py, which makes the strVersionOracle the module name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Parse and format Oracle version.
def formatVersion(s):
 
  # Split string into collection.
  list = s.split(".")
 
  # Iterate through the result set.
  for i, l in enumerate(list):
    if i == 0 and list[i] == "11":
      label = str(l) + "g"
    elif i == 0 and list[i] == "12":
      label = label + str(l) + "c"
    elif i == 1:
      label = label + "R" + list[i] + " (" + s + ")"
 
  # Return the formatted string.
  return label

You can put this in any directory as long as you add it to the Python path. There are two Python paths to maintain. One is in the file system and the other is in Python’s interactive IDLE environment. You can check the contents of the IDLE path with the following interactive commands:

import sys
print sys.path

It prints the following:

['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages']

You can append to the IDLE path using the following command:

sys.path.append("/home/student/Code/python")

After putting the module in the runtime path, you can test the code in the IDLE environment:

1
2
3
import cx_Oracle
db = cx_Oracle.connect("student/student@xe")
print strVersionOracle.formatVersion(db.version)

Line 3 prints the result by calling the formatVersion function inside the strVersionOracle module. It prints the following:

11gR2 (11.2.0.2.0)

You can test the program outside of the runtime environment with the following oracleConnection.py file. It runs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Import the Oracle library.
import cx_Oracle
import strVersionOracle
 
try:
  # Create a connection.
  db = cx_Oracle.connect("student/student@xe")
 
  # Print a message.
  print "Connected to the Oracle " + strVersionOracle.formatVersion(db.version) + " database."
 
except cx_Oracle.DatabaseError, e:
  error, = e.args
  print >> sys.stderr, "Oracle-Error-Code:", error.code
  print >> sys.stderr, "Oracle-Error-Message:", error.message
 
finally:
  # Close connection. 
  db.close()

You can call the formatVersion() function rather than a combination of module and function names when you write a more qualified import statement on line 3, like:

3
from strVersionOracle import formatVersion

Then, you can call the formatVersion() function like this on line 10:

10
  print "Connected to the Oracle " + formatVersion(db.version) + " database."

It works because you told it to import a function from a Python module. The first example imports a module that may contain one to many functions, and that style requires you to qualify the location of functions inside imported modules.

The oracleConnection.py program works when you call it from the Bash shell provided you do so from the same directory where the oracleConnection.py and strVersionOracle.py files (or Python modules) are located. If you call the oracleConnection.py file from a different directory, the reference to the library raises the following error:

Traceback (most recent call last):
  File "oracleConnection.py", line 3, in <module>
    import strVersionOracle
ImportError: No module named strVersionOracle

You can fix this error by adding the directory where the strVersionOracle.py file exists, like

export set PYTHONPATH=/home/student/Code/python

Then, you can call successfully the oracleConnection.py file from any directory:

python oracleConnection.py

The program will connect to the Oracle database as the student user, and print the following message to the console:

Connected to the Oracle 11gR2 (11.2.0.2.0) database.

I hope this helps those trying to create and use Python modules.

Written by maclochlainn

October 19th, 2016 at 11:50 pm

Python for loops

without comments

It’s always interesting to explain a new programming language to students. Python does presents some challenges to that learning process. I think for-loops can be a bit of a challenge until you understand them. Many students are most familiar with the traditional for loop like Java:

for (i = 0; i < 5; i++) { ... }

Python supports three types of for-loops – a range for loop, a for-each expression, and a for-loop with enumeration. Below are examples of each of these loops.

  1. A range for-loop goes from a low numerical value to a high numerical value, like:
  2. for i in range(0,3):
      print i

    It prints the following range values:

    0
    1
    2
  1. A for-each loop goes from the first to the last item while ignoring indexes, like:
  2. list = ['a','b','c']
    for i in list:
      print i

    It prints the following elements of the list:

    a
    b
    c
  1. A for-loop with enumeration goes from the first to the last item while ignoring indexes, like:
  2. list = ['a','b','c']
      for i, e in enumerate(list):
        print "[" + str(i) + "][" + list[i] + "]"

    The i represents the index values and the e represents the elements of a list. The str() function casts the numeric value to a string.

    It prints the following:

    [0][a]
    [1][b]
    [2][c]

This should help my students and I hope it helps you if you’re trying to sort out how to use for loops in Python.

Written by maclochlainn

October 19th, 2016 at 9:02 pm

MySQLdb Manage Columns

without comments

Sometimes trying to keep a post short and to the point raises other questions. Clearly, my Python-MySQL Program post over the weekend did raise a question. They were extending the query example and encountered this error:

TypeError: range() integer end argument expected, got tuple.

That should be a straight forward error message because of two things. First, the Python built-in range() function manages a range of numbers. Second, the row returned from a cursor is actually a tuple (from relational algebra), and it may contain non-numeric data like strings and dates.

The reader was trying to dynamically navigate the number of columns in a row by using the range() function like this (where row was a row from the cursor or result set):

    for j in range(row):

Naturally, it threw the type mismatch error noted above. As promised, the following Python program fixes that problem. It also builds on the prior example by navigatung an unknown list of columns. Lines 16 through 31 contain the verbose comments and programming logic to dynamically navigate the columns of a row.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python
 
# Import sys library.
import MySQLdb
import sys
 
try:
  # Create new database connection.
  db = MySQLdb.connect('localhost','student','student','studentdb')
  # Create a result set cursor.
  rs = db.cursor()
  rs.execute("SELECT item_title, item_subtitle, item_rating FROM item")
  # Assign the query results to a local variable.
  for i in range(rs.rowcount):
    row = rs.fetchone()
    # Initialize variable for printing row as a string.
    data = ""
    # Address an indefinite number of columns.
    count = 0
    for j in range(len(row)):
      # Initialize column value as an empty string.
      datum = ""
      # Replace column values when they exist.
      if str(row[count]) != 'None':
        datum = str(row[count])
      # Append a comma when another column follows.
      if count == len(row) - 1:
        data += datum
      else:
        data += datum + ", "
      count += 1
    # Print the formatted row as a string.
    print data
except MySQLdb.Error, e:
  # Print the error.
  print "ERROR %d: %s" % (e.args[0], e.args[1])
  sys.exit(1)
finally:
  # Close the connection when it is open.
  if db:
    db.close()

There are a couple Python programming techniques that could be perceived as tricks. Line 24 checks for a not null value by explicitly casting the column’s value to a string and then comparing its value against the string equivalent for a null. The MySQLdb returns a 'None' string for null values by default. The if-block on lines 27 through 30 ensure commas aren’t appended at the end of a row.

While the for-loop with a range works, I’d recommend you write it as a while-loop because its easier to read for most new Python programmers. You only need to replace line 20 with the following to make the change:

20
    while (count < len(row)):

Either approach generates output like:

The Hunt for Red October, Special Collectornulls Edition, PG
Star Wars I, Phantom Menace, PG
Star Wars II, Attack of the Clones, PG
Star Wars II, Attack of the Clones, PG
Star Wars III, Revenge of the Sith, PG-13
The Chronicles of Narnia, The Lion, the Witch and the Wardrobe, PG
RoboCop, , Mature
Pirates of the Caribbean, , Teen
The Chronicles of Narnia, The Lion, the Witch and the Wardrobe, Everyone
MarioKart, Double Dash, Everyone
Splinter Cell, Chaos Theory, Teen
Need for Speed, Most Wanted, Everyone
The DaVinci Code, , Teen
Cars, , Everyone
Beau Geste, , PG
I Remember Mama, , NR
Tora! Tora! Tora!, The Attack on Pearl Harbor, G
A Man for All Seasons, , G
Hook, , PG
Around the World in 80 Days, , G
Harry Potter and the Sorcerer's Stone, , PG
Camelot, , G

As always, I hope this helps those looking for clarity.

Written by maclochlainn

April 13th, 2015 at 10:05 pm