Velocity Reviews > Black Magic - Currying using __get__

# Black Magic - Currying using __get__

Michael Spencer
Guest
Posts: n/a

 03-24-2005
Wow - Alex Martelli's 'Black Magic' Pycon notes
http://www.python.org/pycon/2005/pap...c05_bla_dp.pdf

include this gem:
> Functions 'r descriptors
> def adder(x, y): return x + y
> add23 = adder.__get__(23)
> add42 = adder.__get__(42)
> print add23(100), add42(1000)
> 123 1042

This means that you can do (left) currying without a separate curry function
(Of course, google reveals that the idea has been discussed before,
http://mail.python.org/pipermail/pyt...er/038933.html)

Although it's less flexible than a general curry function, 'method currying' is
much faster, e.g., compare two functions for tail-filtering an iterator:

def filtertail(op, iterable):
"""Recursively filter the tail of an iterator, based on its head
Useful for succinct (though not very fast) implementations
of sieve of eratosthenes among other"""
iterator = iter(iterable)
while 1:
head = iterator.next()
yield head
iterator = it.ifilter(curry(op,Missing,head), iterator)

def filtertail2(op, iterable):
"""An alternative to filtertail, using Alex Martelli's observation
that functions are descriptors. Will not work for built-in
functions that lack a __get__ method"""
iterator = iter(iterable)
opcurry = op.__get__
while 1:
head = iterator.next()
yield head
iterator = it.ifilter(opcurry(head), iterator)

using these generator functions, a Sieve of Eratosthenes can be written as:

primes = list(filtertail(operator.mod, xrange(2,N)))
or
primes = list(filtertail2(lambda head, tail: tail % head, xrange(2,N)))

but the second version, using 'method currying' is 4 times the speed, despite
not using the stdlib operator.mod function

def timethem(N):
import time
t1 = time.clock()
p = list(filtertail(op.mod, xrange(2,N)))
t2 = time.clock()
p = list(filtertail2(lambda head, tail: tail % head, xrange(2,N)))
t3 = time.clock()
return t2-t1, t3-t2

>>> timethem(10000)

(3.8331997502475588, 0.7960575994993632
>>> timethem(100000)

(240.68151008019186, 61.818026872130304)
>>>

of course, neither version is anywhere near the most efficient Python
implementation - this is a comparison of currying, not sieving.

BTW, here's the curry function I used (it could probably be faster; I'm not sure
what/where the future stdlib version is)

Missing = Ellipsis
def curry(*cargs, **ckwargs):
fn, cargs = cargs[0], cargs[1:]
if cargs[0] is Missing:
while cargs[0] is Missing: # rightcurry
cargs = cargs[1:]
def call_fn(*fargs, **fkwargs):
d = ckwargs.copy()
d.update(fkwargs)
return fn(*(fargs+cargs),**d)
name = "%s(...,%s)" % (fn.__name__, ",".join(repr(i) for i in cargs))
else:
def call_fn(*fargs, **fkwargs):
d = ckwargs.copy()
d.update(fkwargs)
return fn(*(cargs + fargs), **d)
name = "%s(%s,...)" % (fn.__name__, ",".join(repr(i) for i in cargs))
call_fn.func_name = name
call_fn.curry = True
return call_fn

Michael

 Thread Tools

 Posting Rules You may not post new threads You may not post replies You may not post attachments You may not edit your posts BB code is On Smilies are On [IMG] code is On HTML code is OffTrackbacks are On Pingbacks are On Refbacks are Off Forum Rules

 Similar Threads Thread Thread Starter Forum Replies Last Post Giovanni Gherdovich C++ 2 08-18-2008 12:52 PM Jakub Hegenbart Python 2 11-07-2007 06:16 PM John Perks and Sarah Mount Python 0 03-23-2005 09:17 PM Shalabh Chaturvedi Python 2 02-20-2004 08:26 PM

Advertisments