Saturday, August 13, 2011

Lambda

The subject was discussed in a lightning talk at a recent San Francisco Python meetup.

Although I've tried in the past to understand lambda, for seem reason a real understanding has eluded to the extent where I have only felt lucky if I correctly wrote a lambda.

lambda's are anonymous functions.  It's hard for me to conceive of a real use for them, I've seen them used inside in generators, list comprehensions, and maybe for sort functions.

If you don't like lambdas, you can always use a def method or function.  I suspect you would only want to use a lambda if the function was fairly simple and used in only place or if you were following a example written by someone who likes lambdas.

Here's an example:

lambda a, b: a+b

This will create a function that requires two arguments which are expressed as a, b:  The processing that will be performed will be found in the expression on the right of the colon, in this case a+b.  This expression will be evaluated with the given arguments and the result will be returned.

The equivalent method would be

def add(a,b):
    return a+b

A big difference of course is the when you use def, a name is assigned to the function.  You can assign a name to a lambda by doing this:

add = lambda a, b: a+b

I believe that there is limitation to a lambda in that only one expression can follow the :, so whatever ever you want to do inside your lambda has to be accomplished in a single expression.

Arguments probably follow the same rules as functions...you can pass values or references to values, you can use keyword arguments  as lambda a=5, b, c: a+b+c. 

You can probably use *args or **kwargs if so inclined, I'm guessing.

As far as what you can return, it seems like it could be anything that normal function could return, such as an integer, a string, a list, a tuple.  If you wanted to return more than one value, you'd have to use one of the sequence types or one of the mapping type.  I suppose you could return any type of object, even one of your own definition.  And yes, you can even return another lambda?

if you define
add=lambda a,b: lambda a,b: a,b

this would work
a(1,2)(3,4)

and the result would be
7

but what's the point of that?

Sometime in the future I may add to this entry with examples of using a lambda inside a generator, comprehension, a sort function, or using *args, and **kwargs in the argument list.



Tuesday, August 2, 2011

IronPython

A couple of months ago I purchased and began to read IronPython In Action.  I didn't get far before I became lost due to the frequent references to typical programming methods for .NET, none of which I know the slightest about.  Luckily I found a book on the subject of C# and .NET programming on sale for $1 and bought it.  Now armed with knowledge from that book, it seems like a good time to get back to IronPython.

Instead of returning to IronPython In Action, I'm working from a different book, Professonal IronPython.  Pro IronPython seems to take a more hands-on, practical approach.  I skimmed through much of the first hundred or so pages, basically an introduction to Python and instructions for using IronPython with Visual Studio 2008 Professional, to Chapter 8 where the serious IronPython for .NET begins.

I had been hoping that Pro IronPython would teach IronPython Studio, but it doesn't.  Instead, it teaches IronPython 2.6.  I've got IronPython 2.7 installed and so far the examples work fine and I've found no reason (as of Chapter 8) to get Visual Studio involved.

The actual purpose of this post is to take a little time to discuss one of the programs introduced in Chapter 8, the "Using a Timer Example"


As you can see from the above screen capture, it is a very simple program, basically a form with two buttons and a label.  The stop button changes from stop to start when you press it and stops or starts the little digital clock.

The author of the program inconviently decided that it should be divided into two modules.  This really doubles the work in some senses because most of the imports are repeated in both modules.  One module defines the form and controls and the other instantiates the form, creates event handlers, and runs the application.

The Windows form itself is defined as a class named frmMain that inherits from System.Windows.Forms.Form.  Only one method is created for the class and it's called InitializeComponent(self)

The InitializeComponent method seems to be something like a constructor. All the form attributes are inside the InitializeComponent method.  Properties of the form are defined within the class first of all and only two attributes are specified, "self.ClientSize" and "self.Text".  The self part is just this Python convention referring to the current instance, I think, and the latter part of the attributes are inherited from System.Windows.Forms.Form.

The size is assigned by using a System.Drawing.Size(x,y) class object.  The text accepts an ordinary string.

The buttons and the label are created with a minimum of property overrides and defaults are accepted for most properties.  Two properties, Text and Location, are provided (overidden) for each button and three (Text, Locaiton, and Size) for the label.

Additionally, a timer is created and one property is assigned, Interval.

The two buttons and the label are finally placed on the form with self.Controls.Add(self.controlname) instructions.

The other form does the rest of the work, including instatiating the form, defining the event handlers, and running the form.

The event handling methods are created in this format:

def btnEvent_Title(*args)
    instructions

*args is a list of some kind containing event parameters.  I don't see that the event parameters themselves where used in the methods, but they would like have some include some about the event, perhaps cursor position, time, and who knows what else..

After all the event methods are defined, the form is instantiated, initialized by calling the InitializeComponent method, event methods added to the event loop, and finally the form is executed.

Here are the two modules:

import sys
sys.path.append("C:\\WINDOWS\\Microsoft.NET\\Framework\\v2.0.50727")

import clr

clr.AddReference('System.Windows.Forms.DLL')
clr.AddReference('System.Drawing.DLL')

import System
import System.Windows.Forms
import System.Drawing.Point

class frmMain(System.Windows.Forms.Form):

    def InitializeComponent(self):
   
        self.ClientSize = System.Drawing.Size(350, 200)
        self.Text = 'Using a Timer Example'
       
        self.btnStart = System.Windows.Forms.Button()
        self.btnStart.Text = "&Start"
        self.btnStart.Location = System.Drawing.Point(263, 13)
       
        self.btnQuit = System.Windows.Forms.Button()
        self.btnQuit.Text = "&Quit"
        self.btnQuit.Location = System.Drawing.Point(263, 43)
       
        self.lblTime = System.Windows.Forms.Label()
        self.lblTime.Text = System.DateTime.Now.ToLongTimeString()
        self.lblTime.Location = System.Drawing.Point(13,13)
        self.lblTime.Size = System.Drawing.Size(120,13)
       
        self.objTimer = System.Windows.Forms.Timer()
        self.objTimer.Interval = 500
       
        self.Controls.Add(self.btnStart)
        self.Controls.Add(self.btnQuit)
        self.Controls.Add(self.lblTime)

and

import sys
sys.path.append('C:\\WINDOWS\\Microsoft.NET\\Framework\\v2.0.50727')

import clr

clr.AddReference('System.Windows.Forms.DLL')

import System
import System.Windows.Forms

from frmMain import *

def btnStart_Click(*args):
    if TestForm.btnStart.Text == '&Start':
        TestForm.objTimer.Start()
        TestForm.btnStart.Text = '&Stop'
    else:
        TestForm.objTimer.Stop()
        TestForm.btnStart.Text = '&Start'
       
def btnQuit_Click(*args):
    TestForm.Close()
   
def objTimer_Tick(*args):
    TestForm.lblTime.Text = System.DateTime.Now.ToLongTimeString()

TestForm = frmMain()
TestForm.InitializeComponent()

TestForm.btnStart.Click += btnStart_Click
TestForm.btnQuit.Click += btnQuit_Click
TestForm.objTimer.Tick += objTimer_Tick

System.Windows.Forms.Application.Run(TestForm)

I ran them at the command line with a single call to the second module, which I called listing84.py:

ipy listing84.py

The code is pretty clear but you'll have to do a lot of guessing or research if you want to make a more complicated program.  Supposing you wanted to create another dialog box instead of stopping or starting the clock...  Or supposing you wanted to group create a set of radio buttons...  As I recall the author's only suggestion is create the forms in Visual Studio and then translate them over to IronPython.

Obviously Chapter 8 only scratches the surface of the subject of forms and controls but what was provided okay.  The last part of the chapter deals with developing your own events and I probably wont' blog it.  Chapter 9 deals with Interacting with COM objects, which may be subject for another blog entry, we'll see.

       

Some notes on the os module

Here's Nick Parlantes function List that wraps up some os functions

def List(dir):
    filenames = os.listdir(dir)
    for filename in filenames:
        path = os.path.join(dir, filename)
        print path
        print os.path.abspath(path)

os.listdir() makes a list out of the filenames in the current directory.

os.path.join() joins a directory name to a path name in the proper form for the operating system apparently without regard as to whether or not the directory name is a valid directory. 

os.path.abspath() accepts a filename and prepends the current directory path to it, without regard as to whether or not the named file exists.

This lesson was more or less directed at Unix type operating systems.  I don't have one that works right now except my webfaction account and it's too much trouble to use so I'll have to postpone this part of the exercise for a few weeks until I get my dead drive back from Seagate.

os.path.abspath()

Tuesday, July 26, 2011

More notes on google python video tutorial

Day 2 of the tutorial deals with the re module.  Again, the lecturer goes straight to heart of the matter to demonstrate how re.search work.  He offers a wrapper function that shows how its typically used in a program:

def find(pat, text):
    match = re.search(pat, text)
    if match: print match.group()
    else: print "not found"

re.search returns a match object if a match is found.  If no match is found it returns None.  The boolean value of None is false, the boolean value of a match object is true, and the return value is used to determine the next action.  In the above case, if a match is match, the group method is used to print the matching characters.

He (Nick Parlante) gives some rules:

1. Simple characters match themselves

2. Special characters:
    "."  a period matches anything except newline
   \w   a word character (a-z, A-Z, 0-9)
   \d    a digit (0-9)
   \s    whitespace spaces? tabs?  \S non-whitespaced character
   +    one or more
   *    zero or more

r prefix for a string r"this" stands for raw.  Parlante uses r in from of his patterns.  To match a period character explicity he used '\.'

To illustrate some of the special characters:

>>> find(r':\w+', 'blah blah blah :  :kitten blah blah')
:kitten
>>> find(r':\w*', 'blah blah blah : :kitten blah blah')
:
>>> find(r':.+', 'blah blah blah : :kitten blah blah')
:  :kitten blah blah

\S gets anything except a space and is more similar to . than \s

pull email address from a string with regular expressions

r'\w+#\w+\.\w+'
no...

how about
find(r'[.\w]+@[\w.]+', 'blah nick.p@gmail.com yatta @ ')
yes...

The [] are used to enclose a set of characters to be matched.  Within square brackets, a '.' means only the literal '.' not 'match any character'. 

m = re.search(r'([.\w]+)@([\w.]+)', 'blah nick.p@gmail.com yatta @ ')

lets you group the match so that you can reference each set of parentheses by number.

>>> m.group()
nick.p@gmail.com
>>> m.group(1)
'nick.p'
>>> m.group(2)
'gmail.com'

The number actually refers to the group beginning with the nth left parenthesis, so that parentheses can be nested.

re.findall returns a list of all matches...

>>> re.findall(r'[.\w]+@[\w.]+', 'blah nick.p@gmail.com yatta foo@bar ')
['nick.p@gmail.com', 'foo@bar']

used with parentheses it makes tuples with a value for each group:

>>> re.findall(r'(([.\w]+)@([\w.]+))', 'blah nick.p@gmail.com yatta foo@bar ')
[('nick.p@gmail.com', 'nick.p', 'gmail.com'), ('foo@bar', 'foo', 'bar')]

You can add flags to many re.methods.  They fit into a third argument.  The flags are constants in the re module.

Monday, July 25, 2011

Notes on Google Python Class videos

There is an interesting set of tutorial videos on youtube from Google that teaches Python.  Although it moves very quickly and is obviously intended for experienced programmers, judging by the first day lessons, it goes right to the heart of matter.

The first day class consists of three videos and covers some basics including assignment statements, types, lists, dictionaries, tuples, and a number of important methods.

The idea here isn't to repeat what is in the video lessons but to record a few notes about points that I found exceptionally interesting.

The most interesting point was the presentation on the sorted and sort methods.  Take a list, we'll call it l, that looks like this ['abc','def','aaa','ccc','bbb'] and consider what the two different sort methods do to it.

l.sort() will sort the list in place.  sorted(l) will create a new sorted list.  Otherwise the two functions are identical.  Both allow for the additional keyword arguments cmp, key, and reverse.

The video lecture did not cover the cmp keyword argument, but did cover key.  The explanation of this function might have been the best part of the video.  You assign a function to key that accepts one argument.  That argument will be passed each list element in turn and the one argument function pointed to by key= will evaluate the argument and produce another value that is used as a sort key.

The presenter maintained that a ghost list of the generated sort keys will be created and sorted and the original list reordered on the basis of the sorted ghost list.

What I found crucial was the fact that the key function takes only one argument!

Another point of interest was the presenters assertion that fetching an item from a dictionary is linear, very fast, and efficient due to key hashing.  This was offered as a good reason for using dictionaries!

Another point with dictionaries is that if you try to get a dictionary value through an index, for example d["word"] but "word" isn't in your dictionary, Python will throw a KeyError exception.  However, if you use d.get("word") and "word" doesn't exist as a key, d.get will return None without generating an exception.  If "word" is a key in your dictionary, getting it by index and getting it with the get method return the same values.

Also interesting were the presentations of a couple of file reading features.

The first one he mentioned was that you can specify a line ending type as part of the filemode in the open statement.  Thus open("f.txt","ru") specifies Unix type line endings.  He didn't explain exactly the significance of this but I thought it was an interesting sidelight, worth remembering.

He showed the typical file reading loop that follows an open statement:

for n in f:
    print f

The point is that the read is implied in the loop.

Also mentioned:  rl = f.readlines() reads the entire file into a line by line list, and r = f.read() reads the entires file into a single string.

Somewhere along the line, he mentioned split() and join().  When used properly, join seems to be the opposite operation form split.

Here is my own example: 
>>> n='aaa bbb ccc ddd eee fff'.split()
>>> n
['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']
>>> ':'.join(n)
'aaa:bbb:ccc:ddd:eee:fff'

What happened above is that we split the string into a list, dividing at each space...then joined the list back into a string using ':' as a separator.  I don't see much use for this myself but I've seen it in a number of ProjectEuler solutions so it must have some use.

If you wanted to nest the two expressions, it might look like this
':'.join('aaa bbb ccc'.split())
which is of pretty limited value since you can achieve the same result with
'aaa bbb ccc'.replace(' ','-')

Thursday, July 14, 2011

Built in help from the Python console

In Python 2.6, you can type help("modules") at the command line to generate a columnar list of available python modules.

At the end of the listing, the following instructions are provided:

"Enter any module name to get more help.  Or, type "modules spam" to search for modules whose descriptions contain the word spam."

It is not necessary to import a module to get help.

You can execute the dir("modulename") command on any module to get a list of classes, variables, and methods.  dir("django")

If the module contains packages as in the case of django, you can obtain the same list by typing dir("modulename.packagename").  For example:  help("django.bin") or help("django.utils").

You can even go a step deeper to get help on a specific class, variable, or function by typing help("modulename.packagename.function.")  For example: help("django.utils.version")

These capabilities can be very useful researching the functionality of any module without actually importing it or reading the source code, although in the case of django the explanations don't seem to be adequate in themselves to determine the best use for the functions.