Tao of the Machine

Programming, Python, my projects, card games, books, music, Zoids, bettas, manga, cool stuff, and whatever comes to mind.

Funny...

I'm now #1 at Google for "manga downloads", even though there is no manga available for download on this site. :-) The high ranking is due to this page, which seems to be a popular choice, even though it was created for my own convenience.

Looking at referrer logs, there also seems to be an article at lwn.net that links to me, but I cannot check it out until September 18th (since I have no subscription).

Posted by Hans Nowak on 2003-09-12 21:33:28   {link}
Categories: general

Metaclasses are evil

Well, maybe it's not that bad. They're wonderful toys to play with, and can often to be used to implement very powerful solutions. But I have something against them, and this Cookbook recipe illustrates what.

My main gripe with metaclasses is that many people have difficulty understanding them, yet everybody and their daughter seems to use them, even for trivial problems that could have been easily solved without metaclasses. Why is that? Is it just for purposes of showing off? Or is it because it's like a shiny new toy and people absolutely want to use it, even if it's not necessary?

Look at that Cookbook recipe again. The idea is to have methods return the object itself, which allows for e.g. a list.append that returns the mutated list. Stephan Diehl and Alex Martelli use metaclass solutions. Now, even if you grok metaclasses, their code is hard to understand. Compare Jacek Generowicz's solution. Shorter and simpler, and -- dare I say it? -- more Pythonic. It's not entirely correct, since it doesn't work with non-callable attributes, but that is easily fixed:

class Returner:
    def __init__(self, obj):
        self.obj = obj
    def __getattr__(self, name):
        x = getattr(self.obj, name)
        if callable(x):
            def proxy(*args, **kwargs):
                x(*args, **kwargs)
                return self.obj
            return proxy
        else:
            return x

Example usage with a test class:

# example class with methods and attributes
class Crank:
    def __init__(self):
        self.data = []
        self.magic = 42
    def append(self, x):
        self.data.append(x)
    def __repr__(self):
        return "Crank(%s)" % (self.data,)

c = Returner(Crank())
print c.append(4)   # Crank([4])
print c.append(5)   # Crank([4, 5])
print c.magic       # 42

...the point being that code using metaclasses can often be rewritten in "regular" Python, which is probably more readable as well. And there's less "magic" involved.

Even if metaclasses are powerful, I think they should be used with caution. I don't buy into the "some programmers are producers and some are consumers" argument. Writing code to be used by others is common, but you lose something if that code is hard to understand. Python code is usually very readable and understandable. It is extremely helpful if people can read the third-party code they have to use. Let's not change that and turn Python into an elitist thing. ("Here is some really difficult code that does what you want, you poor newbie. Don't try to understand it.")

According to someone (Guido? Tim?) 99% of Python programmers don't need metaclasses. It sure doesn't seem that way if you look at how many metaclass-based solutions are presented in the newsgroup and elsewhere. I hope people do that because they're experimenting and learning, not because they want to belong to that special 1%. Still, my recommendation would be to just ignore this feature.

Posted by Hans "insert stupid 'i-never-metaclass' pun here" Nowak on 2003-09-12 20:56:02   {link}
Categories: Python

Wax teaser: drag & drop

(Wax 0.1.29) Making a control accept drag & drop is as easy as:

class MyNoteBook(NoteBook):
    def __init__(self, ...):
        ...other code here...
        filedrop = FileDropTarget(self, self.OnDropFiles)
        self.SetDropTarget(filedrop)
    def OnDropFiles(self, x, y, filenames):
        print "Dropped:", filenames
        # do something with filenames...

Currently FileDropTarget takes two arguments, a window (the drop target) and a function (event) which is called when things are dropped. I wonder if I even need the window argument.

Posted by Hans Nowak on 2003-09-11 22:45:59   {link}
Categories: Wax

The csv module

I used the csv module today, for the first time. (In fact, this is the main reason why we updated to Python 2.3 for one of my projects (aka "work").) Knowing very well that good design isn't easy, here are some comments...

I have a CSV file that I want to loop over, line by line. Nothing special. DictReader seems a suitable choice to do this. However, I don't just want the dict with fields and values, I also want the line that was processed. Problem: the csv module has no way of doing this. So I looked for a function that reads a single comma-delimited line... but there is no such beast either. So I was forced to do (not the actual code):

f = open(filename, "r")
lines = f.readlines()[1:]
for line in lines:
    dr = csv.DictReader([line], fieldnames)
    d = dr.next()
    ...do something with d and line...

...which is an ugly hack, IMHO. It's not *terrible*, but it just doesn't feel right to iterate over lines, then create a DictReader for every line (wrapped in a list to make it iterable).

If anyone has a better idea of how to use this, I'd like to hear it.

Posted by Hans Nowak on 2003-09-10 23:08:55   {link}
Categories: Python

Redesign in progress...

I would almost ask people what they think of the new design. But maybe that isn't such a good idea... :-/

I like the dark red, but I'm not sure about the orange. Everything can change at this point. Maybe the site's sections will get different colors.

Posted by Hans Nowak on 2003-09-10 22:52:12   {link}
Categories: general

Microbettas

Last September or so we spawned two bettas. Now, about a year later, the "babies" still aren't full-grown. Probably because this was only our second spawn and we didn't know what the hell we were doing.

Getting bettas to spawn successfully isn't easy, but rearing the fry is even less easy. It takes a reasonable bit of energy and money as well. Fry don't eat chunks or freeze-dried shrimp or pellets (which are usually bigger than them!), so people feed them all kinds of specialty food... microworms, bloodworms, etc. We used live baby brine shrimp, they're even smaller than 3-day old fry, and therefore very hard to see. All kinds of problems can occur at this stage: some fry eat too much, other fry don't get their fair share, the water gets dirty real quick, etc.

In my case, the fry probably remained small due to malnutrition. At least, that's my theory. I have females larger than the males from this spawn, that's why I sometimes call them "microbettas". Still, they seem healthy, and as said before they're a year old now (about as old as the fish sold in stores).

As expected, the colors vary (after all, the mother was blue and the father had all kinds of colors). It's interesting that the biggest male is red (none of the parents had a lot of red). All the others are blue, in various shades, and some have red as well.

Wayne's This and That has a lot of information on bettas. He also did a bunch of spawns. (Orange bettas! emoticon:kwijl)

Posted by Hans Nowak on 2003-09-08 12:21:50   {link}
Categories: bettas

python.org redesign

Here and here are two weblog posts by Tim Parkin, discussing a proposal for a redesign of the www.python.org site. For the impatient, here are the important links:

It was disappointing that some c.l.py regulars slammed the design, often with irrational arguments. Some people apparently feel very strongly about this. It's too bad they display a knee-jerk reaction when seeing a site that actually looks professional.

Of course, they might have been confused by the fact that the sample pages are images, rather than actual HTML. I'd like to see the actual page, so I can see what it looks like in different browsers.

I like the design. The front page is clear and has everything close at hand. The interior page looks looks nice too. Some people mentioned that the contrast (text vs background) was too low. They may have a point. It's true that black text on a white background will be tiring to the eyes in the long run, but since so many sites and programs do that (including mine :-), the Python site might as well.

I think this is a good design, and for what it's worth, I will support it.

Update. Here's one of the message threads in comp.lang.python.

Posted by Hans Nowak on 2003-09-07 22:03:57   {link}
Categories: Python

Entering lambda land

Haskell. A very impressive and interesting language. It's purely functional, meaning its functions do not (and are not allowed to) have side effects. I've heard it described as "the most Pythonic language that is nothing like Python".

Read on for some random thoughts, much like in last month's Ruby posts.

λ. Haskell is lazy. Code like

ones = 1:ones

(which is essentially an infinite list of 1's) is valid. Of course, infinite lists have the slight problem that you cannot display them entirely, but you can get a finite number of elements. head ones (get the first element of ones) and head (tail ones) (get the second element) work, for example.

(One of the Haskell tutorials actually uses this as the first example. Hmm. Starting a tutorial with infinite lists... very interesting. One of the quotes on the Haskell site said something like, "C programmers, it's best to forget everything you know about programming". They were not kidding.)

λ. Some evaluation orders can be quite surprising. square -2 might not do what you think it does. It is not the square of -2; rather, Haskell evaluates it as square - 2, which is an error. Write square (-2) instead.

Also, function calls bind tighter than most (all?) operators. square 2*2 is *not* the same as square 4. Rather, it's (square 2) * 2.

λ. Functions are first-class "objects". Well, this is normal in a functional language, I guess, and also if you're coming from Python. Also, operators are functions too. That means you can write things like

foldr (+) 0 [1, 2, 3, 4]

(compute the sum of the list of numbers). The first argument to foldr is really just any function with two arguments. Compare (session from ghci):

*Test> let add x y = x + y
*Test> foldr add 0 [1, 2, 3, 4]
10

It would be mildly nice if Python had something similar, e.g. if you could write

sum = map((+), numbers)

or something like that. It's not really necessary, but it's a bit more elegant (IMHO) than importing operator and using operator.add, or defining your own function (possibly using a lambda).

λ. Apparently you cannot type a function name on the command line and get something like <function object blah at 0x666>, like in Python or Scheme. If I try it, I get:

*Test> square

No instance for (Show (a -> a))
  arising from use of `print' at <No locn>
In a 'do' expression: print it

λ. Haskell has modules, and they support module.name syntax. Given a module Test with value x and function square, you can do Test.x and Test.square. If a module has a main function, you can run it as a main program or compile it to an executable.

λ. Speaking of compiling: GHC generates huge executables, compared to the Haskell code that goes in it. "Hello world" takes 485K on my machine.

λ. Here's a feature that will appeal to Pythonistas: Haskell uses indentation! It's not mandatory, by the way. Still, you can use it to spread a function definition over multiple lines, nice and readable:

f x =
  case x of
    0 -> 1
    1 -> 5
    2 -> 2
    _ -> 1

The non-indentation equivalent of this is:

f x = case x of 
  { 0 -> 1; 1 -> 5; 2 -> 2; _ -> 1 }

Not unreadable, but not as nice as the former version, I think.

There's another way to define the function above: you define the same function name with different (non-overlapping) arguments. This seems to be a common style in functional programming.

f 0 = 1
f 1 = 2
f 2 = 5
f _ = -1

This example is not so useful; a more realistic example would be:

factorial 1 = 1
factorial n = n * factorial (n-1)

Enough for now. Haskell-buffs, let me know if I made any mistakes, or misunderstood stuff.

Questions (for me) to ponder:

  • Would refactoring be possible in a purely functional language? If so, what would it look like?
  • How would you write an interface from Haskell to an OO system? (E.g. an API?)
  • Have people written OO packages in Haskell? What do they look like?

Posted by Hans Nowak on 2003-09-06 12:02:04   {link}
Categories: programming

--
Generated by Firedrop2.