PYTHON AND HOW TO DO THINGS ONE AT A TIME

I actually wrote a really, really big post for this section, but every
time I tried to submit it, Python’s whitespace formatting made the
result look like shit, so instead, I’m going to show you all how I got
through a little issue.

Although I know a few different names for it, I can’t think of a single
programming language that hasn’t got an equivalent to a “for” loop;
this is a way of doing a single tedious task several times,
occasionally, but not necessarily to each individual in a collection of
things (or each element in an array.)

Python’s equivalent looks like this:

for element in array:

    * block *

Make the indented block show what you want done with each element and
we’re in business.  Perl’s equivalent is “foreach” and Ruby’s is
“array.each { | element | * block * }”.  No problem.

There are even ways to go through the elements of dictionaries (what
Python calls “hashes” or “associative arrays”, arrays which often
have  strings for elements instead of numbers) as if they are
arrays.  The rub was in how I had chosen to use a dictionary in my
program.

The media player amaroK can be instructed to generate and save the
current playlist as a very detailed XML document, one that contains a
lot more than just the file paths of the queued tracks; it even shows
you your current popularity ratings from the database it keeps! 
This means that any changes you may like to perform on the music in a
media player’s playlist can be loaded into a program and run with
amaroK’s own drag-and-drop and search functionality!  I wanted to
write a library that interprets this XML document.

I chose to write it in Python as an exercise.  I just wanted to
learn Python and find out what its own hype is about; most of my
friends that code perceive it as a me-too language (that does nothing
you can’t already do in another language), or a language that is hard
to write in because it has some rather draconian writing
restrictions.  For example, there is no punctuation to show the
end of a block- a block is determined by the size of its indent. 
I don’t hate the resulting look, but I do continue to consider this a
road to Hell paved with a good intention.  Otherwise, I think
Python is…  not bad.

So, I needed to take something that looks like this (this snip represents a single track):

</item>

 <item url=”file:///mnt/cf/Audio/5_Key_Bored.mp3″ >

  <Title>Key-Bored</Title>

  <Artist>Edan</Artist>

  <Album>Primitive Plus</Album>

  <Year>2002</Year>

  <Track>5</Track>

  <Directory>/mnt/cf/Audio</Directory>

  <Length>1:38 </Length>

  <Bitrate>148 kbps</Bitrate>

  <Score>0</Score>

  <Type>mp3</Type>

  <Playcount>0</Playcount>

 </item>


…and get Python to read it in, for use in any program I feel like writing.

Python’s standard library has a way to do this, and the process for it
is rather trivial.  I made the output of the XML reader a
dictionary:  a hash, where all the keys represent each tag you see
in the XML above, and the elements are arrays.  All the ‘0’
elements in these arrays correspond to the first track, all the ‘1’
elements refer to the second track, and so on.  That means that
the title of the first track is read with playlistdict[‘titles’][0].

The problem with this format is that if I want to process each track
one at a time, there is no way I can simply create a “for” loop to do
it without handling each array separately.  The first solution I
thought of was to have nested loops that dealt with each tag for each
track in order, that is, work on all the filenames, then all the
titles, then all the genre tags, etc, but that’s horribly inefficient
and wrong.  That’s not how I would go through it if I had it all
listed on paper to look at, so it’s not how the computer should process
it either.

My second solution was also wrong, but it functioned well enough that I
could run programs, and is actually what I used to fill my PDA with
music when I went on vacation in January.  (KEYS is an array with
all the possible tag keys stored in it.)

    count = -1

    for file in playlistdict[‘files’]:

        count = count + 1

        for key in KEYS:

            * block, do stuff to playlistdict[key][count] *

If you can’t beat ’em, join ’em- I made up my own counter to process
the tracks one at a time, the inner loop’s block acting on each tag for
a single track, in order, because we know that all the tracks have all
the tags, right?

It worked well enough to write programs with it, but I’d occasionally
get these errors where it runs through the entire playlist twice for no
reason, or it would balk on missing tags… so I had to use my programs
cautiously.  It wasn’t until I tried to write a different program
(for organizing and creating audio CDs) that I stumbled across a
less-than-obvious part of the Python documentation which describes how
to write your own iterators for your own object classes!  That
means that if you follow the instructions, you can make any kind of
data you store accessible one at a time, with a single “for” loop.

class iterpldict:

    “””

    Makes an iterator for the playlist dictionaries eg.:

    for dict in iterpldict(PLAYLISTDICT):

        (block)



    With this, you can go through each track entry one at a time.

    It returns {‘files’:URL,’titles’:string,…etc…}

    “””

    def __init__(self, PLAYLISTDICT):    # constructor is shown a playlist

        self.playlistdict = PLAYLISTDICT

        self.index = 0

    def __iter__(self):

        return self

    def next(self):

        output = {}    # start with an empty hash

        try:

            for key in KEYS:

               
output[key] = self.playlistdict[key][self.index]


        except IndexError:

            self.index = 0

            raise StopIteration

        self.index = self.index + 1

        return output


This class makes an object that is “fed” a playlist hash when it is
created, and when run through a “for” loop, it it gives us a much
simpler hash to run through instead, one that pertains to a single
track instead.

Any class can be used this way as long as it has a definition for 
a method “__iter__” which simply returns the object itself, and a
definition for a method “next” which returns whatever data you would
want a “for” loop to process.  As long as your “next” method
eventually raises the exception called “StopIteration” your loop will
end, and it doesn’t even matter how you reach the conclusion that this
should be done.  In my code above, the loop will stop when there
are no more elements left to access.  (That’s what the “try” block
is for.)

As a result, my programs look quite a bit easier to read, though there
is still a nested “for”, there is no dodgy counter.  All I need is
this:

    for track in iterpldict(playlistdict):

        for key in KEYS:

            * block, working on track[key] *



Now, if the class itself didn’t seem like such a kludge I’d be happier about the Python language…

STUDY: USA HIGH SCHOOL STUDENTS DON’T KNOW ANYTHING ABOUT PERSONAL FINANCE


Now, this bit will be a little unfair:  you see, I was put on the
“college prep” track in high school because it was believed that it was
the only hope for getting a well-paying job.  Right… 
anyway…

I do not recall a single class in high school that spent a significant
amount of time explaining how to manage a bank account, write a check,
or interpret loan debt.  Not one.  Not that we didn’t spend
three seconds in a math class occasionally explaining how to calculate
the percentage of sales tax, and not that I couldn’t eventually deduce
how to fill out my checks when it finally mattered, but that’s not
quite the point I’m making, nor the point of the study.

The students were asked which was the highest yielding investment after
eight years, given the choice of stocks, CDs (meaning certificates of
deposit, but I’m not certain if it was explained as such), US treasury
bonds, and a statement savings account.  Most were wrong, and the
most common answer was bonds.  Heinous market collapses
notwithstanding, the correct answer is stocks.

My point is that the reason these kids didn’t know the answer is because it is not taught in school.

If we didn’t carry the pretense that school “prepares you for life” or
“opens careers” or teaches you “how to survive in the world” I wouldn’t
be so bothered by it.

Don’t have a lot of other recent news.  The reason I’m writing an
audio CD sorter and writer is because once the weather starts getting
hot down here it will be unsafe to leave my Zaurus in the car, so now I
can have random/shuffled CDs created instead.

Work is in Spring/Summer lull, but recent events have me working a 50
hour week soon, anyway.  The sudden workload is a combination of
certain people being ill at the same time as some new clients we serve
are having training sessions, so things are going alright.

Hope everyone is well.

See you next time.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s