April 22, 2008

Method signature type checking decorator for Python 3000

I have just published a Python 3000 decorator for method signature type checking using function annotations. Here:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/572161

It is much cleaner that the similar decorator I have previously written for Python 2.x, the used Python 3000 function annotations make it better for the following reasons:

1. The signature-related piece of syntax is right there where it belongs - next to the parameter. Where I used to write
@takes(int, str)
@returns(bool)
def foo(i, s):
...
I now write
@typecheck
def foo(i: int, s: str) -> bool:
...
2. I don't have to add checking to all the parameters simply because there was no way to skip one. Where it was
class Foo(object):
@takes("Foo", str)
def foo(self, s):
...
it is now
class Foo:
@typecheck
def foo(self, s: str):
...
3. It plays nicely with the default values. This one has no equivalent in 2.x version, but it is nice to have:
@typecheck
def foo(x: int = 10): # 10 is also checked
...

@typecheck
def foo(*, k: optional(str) = None):
...
Other than that, it is just a nice usable piece of code, extensible too. Here is a few more examples:
@typecheck
def foo(x: with_attr("write", "flush")):
...

@typecheck
def foo(*, k: by_regex("^[0-9]+$")):
...

@typecheck
def swap_tuple(x: (int, float)) -> (float, int):
...

@typecheck
def swap_list(x: [int, float]) -> [float, int]:
...

@typecheck
def extract(k: list_of(by_regex("^[a-z]+$")),
d: dict_of(str, int)) -> list_of(int):
...

April 21, 2008

Approaching Python 3000: no more automatic tuple parameter unpacking

Just keep in mind that
lambda (x, y): x + y
is no longer possible in Python 3000. You are supposed to write
lambda x_y: x_y[0] + x_y[1]
instead. This also applies to functions, not just to lambdas and the idea belongs to PEP 3113 which forbids automatic tuple unpacking in function parameters. Ugly and inconvenient if you ask me, but there apparently was somebody who kept shooting himself in the leg.

I think that automatic unpacking was rather useful (if used sparingly), especially when you had to do something like
map(lambda (i, (a, b)): i * (a + b),
enumerate(zip(aa, bb))
which is now what ?
map(lambda i_a_b: i_a_b[0] * (i_a_b[1][0] + i_a_b[1][1]),
enumerate(zip(aa, bb))
Eeew...