Efectos Especiales

Ramblings, rants, musings, ideas and observations. Topics include (but are not limited to): programming (especially Python), books, games (especially CCGs and board games), astrology, design, writing, painting, etc.

icon:default #659 (old comments) Boem

The other day, Windows XP decided it would not serve me anymore. Lose, lose. I haven't seen blue screens of death since Windows 98... didn't even know they still existed. Anyway, so a reformat was in order.

I could go on a long rant about how much work it is to install everything again, and how Windows fights you tooth and claw during that process, but that has already been done here.

I never really believed those stories where people install Windows XP, go online and immediately are infested with viruses and trojans. Until today. XP is so full of holes, it's not even funny. Of course we knew that, but experiencing it first hand is something else entirely. And this was with service pack 1 installed. Boom, before I knew it I had lots of mysterious processes running, and I found a total 25 entries in the "start when computer starts" sections of the registry, 3 of which actually belonged there. (It's times like these that tools like reginspector.py are very valuable.)

Windows 2000 seemed safer and more stable... in 2.5 years of using it, I never had a BSOD, nor did I get pummeled by viruses as much as on Windows XP in 6 months. (Or one day, for that matter...) I tried installing it, but it didn't like my DSL. Oh well. Poep gebeurt.

On the flip side, this is a good opportunity to install some shiny new software. An anti-virus program ripped up my Pegasus mailbox, so it's time for a new mail program... Mozilla Thunderbird seems promising. For browsing, Firefox 0.9.

[Update #1] Now if I could only get those damn mail filters to work in Thunderbird...

/* Posted by Hans Nowak at 2004-10-17 21:47 */

icon:python #658 (old comments) Class methods and inheritance

When hacking on production code (aka "work"), I ran into an interesting issue.

I am writing a specialized, lightweight object-relational mapper. There's a base class SQLMap that implements a number of common actions... insert a record, update it, set fields, etc. It has a class method load() that reads a record from the database, turns it into an object, and returns it. To do so, it uses data defined in the class in question, like __fields__, __key__ and __table__.

Class X derives from SQLMap and doesn't define its own load. X.load() reads from a table, and returns an instance of X. It uses data (__fields__, etc) defined in X. So far, so good.

For class Y, things are bit more complicated. I still want the functionality of the default load, but I want to do something more with the object before it's returned. If load was a regular (instance) method, it would look something like this:

class SQLMap(object):
    def load(self, db, id):
        obj = self.__class__()
        # ...do something with obj...
        return obj

class Y(SQLMap):
    def load(self, db, id):
        obj = SQLMap.load(self, db, id)
        # ...do something with obj...
        return obj

However... load() is a classmethod, so that's not going to work. For SQLMap, it looks like this (simplified of course):

class SQLMap(object):
    def load(cls, db, id):
        obj = cls()
        # ...do something with obj...
        return obj
    load = classmethod(load)

How to define Y.load? The following seems obvious, but it doesn't work as intended:

class Y(SQLMap):
    def load(cls, db, id):
        obj = SQLMap.load(db, id) # note: no cls!
        ...do something with obj...
        return obj
    load = classmethod(load)

SQLMap.load() uses the data in SQLMap, rather than in Y. So it uses SQLMap.__fields__, etc, which makes sense, but it's not what I want in this situation.

The following is a workaround... rather ugly, I think:

class Y(SQLMap):
    def load(cls, db, id):
        obj = SQLMap.load.im_func(cls, db, id)
        ...do something with obj...
        return obj
    load = classmethod(load)

I wonder if it's not better to just abandon class methods altogether here, and go with functions at the module level. That doesn't require any funky tricks with im_func, and I can easily pass the appropriate class.

I wonder if I'm missing something (as usual emoticon:nosmile) and that there's a better way that I'm not aware of...

[Update #1] Changed the example code so that SQLMap derives from object, to avoid confusion. The real (production) SQLMap already did derive from object.

[Update #2] Some people are skeptical. emoticon:smile This is not a bug, a wart, or a misfeature. SQLMap.load(db, id) looks things up in SQLMap, as it should... after all, it has no way to look things up anywhere else. It's not like an instance method where you implicitly pass self. The class that is implicitly passed is SQLMap. So, I consider this behavior normal and explicable; it's just that it's undesirable in this particular case, and I have to work around it using im_func, which feels like a hack. ... That's all.

[Update #3] Here's some actual code. I should have posted this in the first place.

class SQLMap(object):
    __table__ = "" # override in subclasses
    def load(cls, db, id):
        obj = cls()
        obj.msg = "I loaded my data from " + repr(cls.__table__) + "!"
        return obj
    load = classmethod(load)

class X(SQLMap):
    __table__ = "employee"

o1 = SQLMap.load(None, None)
print type(o1), o1.msg # prints SQLMap,  ''
o2 = X.load(None, None)
print type(o2), o2.msg # prints X, 'employee'

class Y(SQLMap):
    __table__ = "salary"
    def load(cls, db, id):
        obj = SQLMap.load(db, id)
        # ...do some more stuff...
        obj.data = "blah"
        return obj
    load = classmethod(load)

o3 = Y.load(None, None)
print type(o3), o3.msg # SQLMap, table ''

This prints:

<class '__main__.SQLMap'> I loaded my data from ''!
<class '__main__.X'> I loaded my data from 'employee'!
<class '__main__.SQLMap'> I loaded my data from ''!

[Update #4] This can be done with super, as pointed out by Zachery Corbiere. Witness:

class Z(SQLMap):
    __table__ = "addresses"
    def load(cls, db, id):
        obj = super(Z, cls).load(db, id)
        obj.data = "blah"
        return obj
    load = classmethod(load)

o4 = Z.load(None, None)
print type(o4), o4.msg # prints Z, 'addresses'

Cool, I didn't know super could be used in class methods as well. In fact, I never really used it. emoticon:bloos

/* Posted by Hans Nowak at 2004-10-14 20:00 */

icon:adventures #657 (old comments) A tad of TADS [1]

I'm checking out TADS, a system to write interactive fiction (aka text adventures). This is the first in a series of posts (I hope) that demonstrate how an adventure is written using TADS' specialized language. Basically, I'll write down what I've learned.

Well, here is some simple example code...

#include <adv.t>
#include <std.t>

startroom: room
    sdesc = "Outside cave"
    ldesc = "You're standing in the bright sunlight just outside of a
             large, dark, forboding cave, which lies to the north.   "
    north = cave
;

cave: room
    sdesc = "Cave"
    ldesc = "You're inside a dark and musty cave.  Sunlight pours in
            from a passage to the south."
    south = startroom
;

That's all for now. Notice that it's really easy to set up a bunch of rooms and connect them. More in the next installment, where we add objects to the mix.

[1] Or maybe it's prototype-based... not sure yet. At this point, I'm just walking through a tutorial, which doesn't discuss the finer points of the language.

/* Posted by Hans Nowak at 2004-10-13 23:30 */

icon:nl3 #656 (old comments) Come on now

The UK had 100 Greatest Britons. Germany had Unsere Besten. Now the Netherlands have De Grootste Nederlander.

In these shows, you vote for illustrious, "great" people who played an important role in your country's history. Often, these are great rulers, inventors, artists, etc. In Britain, the winner was Churchill, and the top ten contains people like Darwin, Shakespeare and Newton. In Germany, the winner was Konrad Adenauer, with Martin Luther, Karl Marx, Bach, Goethe and Einstein in the top as well.

The top ten "great Dutch" are known now, although the exact order has yet to be determined. I was dismayed and appalled to find Pim Fortuyn there. IMHO, it's just a bad case of "famous when you're dead". 8 of the 10 Dutch in the top ten are there because of merit. Football (or, for you Americans, soccer) player Cruijff, painters Van Gogh and Rembrandt, scientist Van Leeuwenhoek, ruler Willem van Oranje (aka William the Silent), admiral Michiel de Ruijter, politician Drees, philosopher Erasmus. Anne Frank didn't really "do" anything "great", but her diary is known all over the world, and she has become a symbol of tolerance and resistance against racism.

Next to these nine, the merits of Fortuyn seem very bleak. An economist-turned-politician, he gained a lot of popularity in 2001 and 2002, with his populist and controversial viewpoints, often bordering on racism and misogyny. Then, shortly before the elections in 2002, he was shot. A black day for Dutch democracy, certainly. But just because someone was shot, shouldn't be a reason to consider him for a list of "great Dutch". Think about it: If he was still alive, would he be on that list? Most likely not.

I suppose he could be considered a symbol too, much like Anne Frank. But where Anne Frank stood for tolerance, Fortuyn stood for intolerance. emoticon:nosmile

I sure hope that he won't be chosen as the #1... that would be quite a disgrace. Imagine the French choosing Jean-Marie Le Pen as the "Greatest Frenchman", or the Americans choosing David Duke.

[Update #1] Added a bunch of Wikipedia links.

/* Posted by Hans Nowak at 2004-10-11 20:44 */

icon:adventures #655 (old comments) That maze of twisty little passages, again

The other day, Slashdot had an article about the annual IF Competition.

I haven't written interactive fiction since the middle 90s. emoticon:nosmile I always wanted to write another text adventure, but for some reason it just never happened. Possibly because I usually ended up trying to write an IF system first (in Python or otherwise), then lost interest along the way. I wrote a few times about my attempts, IIRC.

Maybe this is a good time to look at a non-Pythonic solution, like TADS. I would have to learn the system and language, but at least I wouldn't have to build it from scratch (although that would be fun in its own right).

I'm a check it out. Until then, here's something to read: Crimes Against Mimesis.

[Update #1] This line on the TADS overview page especially caught my eye: "The language has strong typing at run-time for easy error detection, but dispenses with compile-time type declarations, so program entry is quick and modification is easy." Sounds good...

/* Posted by Hans Nowak at 2004-10-04 18:55 */

icon:default #654 (old comments) On voting

Some thoughts. (Mostly inspired by this article by Jason Orendorff.)

1. Your vote doesn't count any more than that your spitting in the sea makes the water level rise. If you don't vote, it very likely won't make any difference, unless in your state the two major candidates happen to have exactly the same number of votes.

2. However, if many people choose not to vote, then it *will* make a difference. That may happen if a lot of people feel their vote isn't important. Ironically, in such a situation, their collective withdrawal of voting will make a difference.

3. I believe you should vote for the candidate that you think is best. Whether they have a chance of winning the current elections is unimportant. If you don't vote for candidate X because "they will never win", then you're creating a self-fulfilling prophecy... they will never win because people don't vote for them. And a vote for such a candidate isn't "thrown away"; a vote for a candidate that you don't really support, *is*.

4. Having only two relevant parties may or may not be a problem. It's a bit ironic though... in the US you can choose between *tons* of brands for just about anything. Strange that in the country of unlimited choice, there's only two political parties that count.

5. Being Dutch, I like the Dutch election system better... to some extent, anyway. There are more parties to choose from. It's less black-or-white. From a Dutch point of view, there is very little to choose in the US... all we see is two parties that are both on the right side of the political spectrum. (In fact, we don't even get to see the parties, just a show with two people. Invariably, those two people are male, white, rich, at least middle-aged, heterosexual, and married.)

6. That said, the Dutch system of multiple parties has some serious drawbacks too. You vote for a person, but the party gets the vote, and whether that person will actually take seat in the parliament is yet to be seen (because that decision will be made by their party). Also, after the elections, when a coalition needs to be formed, it's not uncommon that parties play dirty games to keep others out of the coalition. And to be fair, we haven't had a female prime minister yet either.

7. But the fatal flaw in both election systems is, IMHO, that people have so little power, even as a collective. Every N years, you vote for a party or person. That's it. After your vote, the winners can do pretty much whatever they want. The US people didn't vote to go to war in Iraq, nor did the Dutch people vote to support the US. When asked, they might have chosen to do so or not. But the point is, they weren't asked. Should they be? Or is it alright that a select few get to do whatever they want, even if it's potentially against the will of the majority?

8. On a side note, I'm not allowed to vote in the US, because I'm not a US citizen. Not a big deal really, there is nobody I would vote for anyway. emoticon:nosmile

[Update #1] More thoughts on voting here.

/* Posted by Hans Nowak at 2004-10-04 11:40 */

icon:hurricane #653 (old comments) Back in business

Well, this one wasn't so bad... at least not for us. This time, the power was only out for two days. As far as I can tell, more trees came down, but there was actually less damage to buildings and power lines.

I could insert a sarcastic line about stupid power companies that put their power lines right next to trees, so the next time there is some wind the lines will be hit again... but I won't. emoticon:smile

I do wonder how it's possible that the power company is effectively a monopoly. Sure, there are other ones, but the areas are divided up between them (the word "cartel" comes to mind), so here you either get Clay Electric or nothing. The other companies don't serve this area. So, you really have no choice (short of moving, after which you will have to put up with the one power company in *that* area). A strange thing to see in a capitalist country like the US.

Anyway, let's hope this will be the last hurricane for a while.

/* Posted by Hans Nowak at 2004-09-28 11:58 */

icon:magicthegathering #652 (old comments) Gatherer

There is a new searchable online database for Magic cards: Gatherer.

For example, here's a list of all Falcons. [1]

Multiple output formats are possible. For example, here are all 42 Angels in text spoiler view, a format that could probably be parsed by third-party tools. Ditto for all 57 Dragons in text checklist view (which has less info).

(There are also three cards with Hans in the flavor text, but even the "full" spoiler views won't show it. emoticon:smile Fortunately, you can click on the card names for more info.)

[1] Plus Soraya the Falconer.

/* Posted by Hans "Ach! Hans, run! It's the Lhurgoyf!" Nowak at 2004-09-25 23:11 */

icon:python #651 (old comments) ElementTree, my dear Watson

Praise where praise is due... The ElementTree package is damn easy to use.

I am writing a small tool that scans directories and "does something" with the files found... move them elsewhere, delete them, stuff like that. For the configuration, I decided to use an XML file. I don't like XML all that much, but it's standard and beats having to write your own format or mini-language.

Rather than going with the default Python XML packages, I decided to give ElementTree a try. I don't regret it... it's extremely easy to use. Granted, my XML file isn't very complex, so the XML parser doesn't need to do any heavy-duty work. I have a feeling it would shine even more with complex XML, though.

Some quick examples. You just feed the file to the parser, then get the root:

tree = ElementTree.parse(filename)
root = tree.getroot()

Now that you "got root", you can easily walk over all the elements. Use the tag attribute to get the element's name.

for elem in root:
    tag = elem.tag.lower()
    if tag == 'this':
        ...do something...
    elif tag == 'that':
        ...do something else...
    else:
        print "Unknown tag:", elem.tag

Attributes can be retrieved with the get method. Subelements can be retrieved by iterating over the element.

if tag == 'directory':
    path = elem.get("path")
    # examine sub-elements
    for subelem in elem:
        ...do something nice...

This functionality was all I needed for my XML file. All this I found out in a few minutes by inspecting the appropriate objects (root, elem) and trying a few things. Put *that* in xml.dom.minidom's pipe and smoke it. emoticon:smile

IMHO, this is how Python software should be written, not by implementing large and overengineered specifications borrowed from Java.

/* Posted by Hans Nowak at 2004-09-25 16:37 */

icon:hurricane #650 (old comments) Here we go again

Hurricane Jeanne is expected to make landfall in Florida today or tomorrow. And this time, the projected path leads straight over Gainesville. So if I don't write for a week or so, y'all know the reason. emoticon:nosmile

[Update #1] You'd think that because of all the power outages, you'd at least have a smaller electric bill, which would make up a little for the loss of work... but now I hear that the electric company plans to charge people this month with "an estimate based on the bills over the past year" (as opposed to an amount based on what you actually used, which is what they usually do). Isn't the US great?

[Update #2] But Jason Orendorff argues that price gouging is your friend. Some interesting points here. I think it applies more to other kinds of price spikes than what Clay Electric is doing, though.

I'm not sure I agree with the point "It helps direct limited resources to those who need them the most." No, it helps direct the resources to those who can afford them. If "John" doesn't have the $400 for a generator, but "Bob" does, that doesn't tell me anything about who *needs* it most.

[Update #3] At this moment (2004-09-26, 12:10 PM) we still have power and everything, but the hurricane isn't done yet.

/* Posted by Hans Nowak at 2004-09-25 11:20 */