In order to get a better feeling for what can be done with functools.partial() I am taking some “real world” python code of mine and refactoring it to use curried functions.

In the example below the construct_getopt_data() function (lines 7-17) takes the data structure shown on lines 44-54 (below) and returns a 2-tuple where

the first element is a string of option letters (second argument to getopt() (see variable shortflags in the output below)) and

(see variable in the output below)) and the second element is a list of strings with the names of long options (third argument to getopt() (see variable longflags below))

The resulting data is thus as follows:

mhr@playground2:~/src/published$ python2.5 partialf.py shortflags = 'l:ep:a:x:r:o:i:dc' longflags = ( 'lines=', 'echo', 'fyo', 'pager=', 'pgr=', 'algo=', 'recipient=', 'decrypt', 'crypt') >> test ok

And the code that’s producing the output above looks as follows:

1 #!/usr/bin/env python 2 from pprint import PrettyPrinter as PP 3 from itertools import (imap, repeat) 4 from functools import partial 5 from operator import (ge, lt) 6 7 def construct_getopt_data (args): 8 """ uses lambdas """ 9 # single and multi-character flag iterators 10 shortiter = lambda args: argiter(args, lambda s: s <= 2) 11 longiter = lambda args: argiter(args, lambda s: s > 2) 12 13 # single character flags 14 shortfs = imap(formatf, shortiter(args), repeat( ' : ' )) 15 # multi-character flags 16 longfs = imap(formatf, longiter(args), repeat( ' = ' )) 17 return ( '' .join(shortfs), tuple(longfs)) 18

Please note:

the function above uses the lambda construct (on lines 10-11) to customise the short and long argument iterator respectively.

construct (on lines 10-11) to customise the short and long argument iterator respectively. the use of itertools.repeat() to supply the fchar parameter to the formatf() function (lines 14 and 16)

The analogous function construct_getopt_data2() (lines 19-29) below performs the same taks but uses curried functions as opposed to lambdas.

19 def construct_getopt_data2 (args): 20 """ uses functools.partial """ 21 # single and multi-character flag iterators 22 shortiter = partial(argiter, op=partial(ge, 2)) 23 longiter = partial(argiter, op=partial(lt, 2)) 24 25 # single character flags 26 shortfs = map(partial(formatf, fchar= ' : ' ), shortiter(args)) 27 # multi-character flags 28 longfs = map(partial(formatf, fchar= ' = ' ), longiter(args)) 29 return ( '' .join(shortfs), tuple(longfs)) 30

It utilises functools.partial()

on lines 22-23: to customise the argument iterator by currying the built-in operator functions operator.ge() (greater or equal) and operator.lt() (less than) using these curried operator functions to preset the op parameter of the argiter() generator on lines 26 and 28 to preset the fchar parameter of the formatf() function (the ruse with itertools.repeat() is hence not needed any more)

31 def argiter (args, op): 32 """ pair short/long flags will their respective types """ 33 for flags, argdata in args.iteritems(): 34 for flag in flags: 35 if op(len(flag)): yield (flag, argdata[1]) 36

The argiter() generator above facilitates the iteration over the input data structure (lines 44-54) in (single character flag, type) and (multi-character parameter, type) pairs respectively.

37 def formatf ((argn, argt), fchar): 38 """ format for getopt(), 39 argn is the flag, argt is its type, fchar is one of ':' or '=' """ 40 return argt == bool and argn.lstrip( ' - ' ) or " %s%s " % (argn.lstrip( ' - ' ), fchar) 41

The formatf() function above returns the single and multi-character command line flags in the format required by getopt() .

42 if __name__ == ' __main__ ' : 43 # dictionary with command line args along with their types and defaults 44 args = { 45 ( ' -a ' , ' -x ' , ' --algo ' ) : ( ' algo ' , str, None), 46 ( ' -c ' , ' --crypt ' ) : ( ' crypt ' , bool, None), 47 ( ' -d ' , ' --decrypt ' ) : ( ' decrypt ' , bool, None), 48 ( ' -e ' , ' --echo ' , ' --fyo ' ) : ( ' echo ' , bool, None), 49 ( ' -l ' , ' --lines ' ) : ( ' lines ' , int, ' 25 ' ), 50 ( ' -i ' , ) : ( ' input ' , str, None), 51 ( ' -o ' , ) : ( ' output ' , str, None), 52 ( ' -p ' , ' --pager ' , ' --pgr ' ) : ( ' pager ' , str, ' /usr/bin/less ' ), 53 ( ' -r ' , ' --recipient ' ) : ( ' recipient ' , str, None) 54 } 55 pp = PP(indent=4) 56 sfs1, lfs1 = construct_getopt_data(args) 57 sfs2, lfs2 = construct_getopt_data2(args) 58 print " shortflags = " , pp.pformat(sfs1) 59 print " longflags = \

" , pp.pformat(lfs1) 60 61 if (sfs1 == sfs2) and (lfs1 == lfs2): print " \

>> test ok " 62 else : print " \

>> test failed "

In conclusion

While functools.partial() is certainly an interesting and cool addition to the python toolchest it would appear that it’s not indispensible for functional style programming.

I would love to see examples or code snippets that are made possible and/or improved greatly by leveraging functools.partial() .