Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Black Magic - Currying using __get__

Thread Tools

Black Magic - Currying using __get__

Michael Spencer
Posts: n/a
Wow - Alex Martelli's 'Black Magic' Pycon notes

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,

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 =
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 =
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)))
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()
return fn(*(fargs+cargs),**d)
name = "%s(...,%s)" % (fn.__name__, ",".join(repr(i) for i in cargs))
def call_fn(*fargs, **fkwargs):
d = ckwargs.copy()
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


Reply With Quote

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 Off
Trackbacks are On
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
Function adapters, currying: how to build a generalized binder? Giovanni Gherdovich C++ 2 08-18-2008 12:52 PM
Strange behavior of __get__ in a descriptor in IPython Jakub Hegenbart Python 2 11-07-2007 06:16 PM
Use of descriptor __get__: what defines an object as being a class? John Perks and Sarah Mount Python 0 03-23-2005 09:17 PM
Descriptors: why __get__(obj,typ=None) instead of __get__(obj,typ) Shalabh Chaturvedi Python 2 02-20-2004 08:26 PM