This is something that I would’ve touched on in my Introduction to Functional Programming in Python talk had it been about 4 hours long.
Suppose I make a nice healthy currying function:
def curry(f, l) : return lambda *r : f(l, *r)
and then use it to define a max function like this:
max=curry(reduce, lambda x,y : [x,y][x<y])
(in Python 2.5 instead of defining curry you can go «
curry = functools.partial»)
(Aside: I really don’t know what to think of that code on the right hand side of the lambda. It’s more or less forced on me by the fact that that body of a lambda is an expression so I can’t use
if. It’s also a compelling reason why Python will probably continue to use Iverson’s Convention for some while.)
I’m not saying that it’s necessarily good idea to define max like this, it’s a bit of a contrived example, but when you get used to higher order functions you end up defining a lot of really useful functions like this instead of using
def statements. Sometimes such a function will be sufficiently useful that it will form part of a public interface, in which case it needs documenting.
The documentation for max is rubbish:
>>> help(max) Help on function <lambda> in module __main__: <lambda> lambda *r
The way to fix that is to give max an explicit docstring via its secret __doc__ attribute and a proper name via its __name__ attribute:
max.__doc__="""Return largest element of sequence.""" max.__name__='max'
Now the help looks like this:
>>> help(max) Help on function max in module __main__: max(*r) Return largest element of sequence.
The __name__ attribute means that max’s name correctly appears after «help on function» and in the function prototype. The __doc__ attribute appears as max’s description.
The way the argument list appears is slightly misleading, but I’ll leave it at that for now. Maybe there’s also a sneaky way to fix the argument list?
It occurs to me that
curry could produce some sort of docstring automatically:
def okname(x) : """Return some okay name for any object.""" def isnum(x) : """Return true if x is a number.""" try : return complex(x) == x except Exception : return False def isstr(x) : """Return true if x is a string.""" try : return str(x) == x except Exception : return False if isnum(x) or isstr(x) : return repr(x) try : if x.__name__ == '<lambda>' : return 'some anonymous lambda' else : return x.__name__ except Exception : return 'some nameless object' def curry(f, l) : r = lambda *r : f(l, *r) r.__doc__ = ('Curried application of ' + okname(f) + ' to ' + okname(l)) return r
The documentation is not great, but it is slightly better than nothing:
>>> help(curry(reduce, operator.add)) Help on function <lambda> in module __main__: <lambda> lambda *r Curried application of reduce to add >>> help(curry(operator.add, 1)) Help on function <lambda> in module __main__: <lambda> lambda *r Curried application of add to 1 >>> map=curry(reduce, lambda x,y : [x,y][x<y]) >>> help(map) Help on function <lambda> in module __main__: <lambda> lambda *r Curried application of reduce to some anonymous lambda
Along with the compiler module, the possibilities are endless.