Following the acceptance of this PEP, further discussion on python-dev andcomp.lang.python revealed a desire for several tools that operated onfunction objects, but were not related to functional programming. Ratherthan create a new module for these tools, it was agreed[1] that the“functional” module be renamed to “functools” to reflect its newly-widenedfocus.
References in this PEP to a “functional” module have been left in forhistorical reasons.
This proposal is for a function or callable class that allows a newcallable to be constructed from a callable and a partial argument list(including positional and keyword arguments).
I propose a standard library module called “functional”, to holduseful higher-order functions, including the implementation ofpartial().
An implementation has been submitted to SourceForge[2].
Patch #941881 was accepted and applied in 2005 for Py2.5. It isessentially as outlined here, a partial() type constructor bindingleftmost positional arguments and any keywords. The partial object hasthree read-only attributes func, args, and keywords. Calls to the partialobject can specify keywords that override those in the object itself.
There is a separate and continuing discussion of whether to modify thepartial implementation with a __get__ method to more closely emulatethe behavior of an equivalent function.
In functional programming, function currying is a way of implementingmulti-argument functions in terms of single-argument functions. Afunction with N arguments is really a function with 1 argument thatreturns another function taking (N-1) arguments. Function applicationin languages like Haskell and ML works such that a function call:
fxyz
actually means:
(((fx)y)z)
This would be only an obscure theoretical issue except that in actualprogramming it turns out to be very useful. Expressing a function interms of partial application of arguments to another function can beboth elegant and powerful, and in functional languages it is heavilyused.
In some functional languages, (e.g. Miranda) you can use an expressionsuch as(+1) to mean the equivalent of Python’s(lambdax:x+1).
In general, languages like that are strongly typed, so the compileralways knows the number of arguments expected and can do the rightthing when presented with a functor and less arguments than expected.
Python does not implement multi-argument functions by currying, so ifyou want a function with partially-applied arguments you wouldprobably use a lambda as above, or define a named function for eachinstance.
However, lambda syntax is not to everyone’s taste, so say the least.Furthermore, Python’s flexible parameter passing using both positionaland keyword presents an opportunity to generalise the idea of partialapplication and do things that lambda cannot.
Here is one way to do a create a callable with partially-appliedarguments in Python. The implementation below is based on improvementsprovided by Scott David Daniels:
classpartial(object):def__init__(*args,**kw):self=args[0]self.fn,self.args,self.kw=(args[1],args[2:],kw)def__call__(self,*args,**kw):ifkwandself.kw:d=self.kw.copy()d.update(kw)else:d=kworself.kwreturnself.fn(*(self.args+args),**d)
(A recipe similar to this has been in the Python Cookbook for sometime[3].)
Note that when the object is called as though it were a function,positional arguments are appended to those provided to theconstructor, and keyword arguments override and augment those providedto the constructor.
Positional arguments, keyword arguments or both can be supplied atwhen creating the object and when calling it.
Sopartial(operator.add,1) is a bit like(lambdax:1+x).Not an example where you see the benefits, of course.
Note too, that you could wrap a class in the same way, since classesthemselves are callable factories for objects. So in some cases,rather than defining a subclass, you can specialise classes by partialapplication of the arguments to the constructor.
For example,partial(Tkinter.Label,fg='blue') makes TkinterLabels that have a blue foreground by default.
Here’s a simple example that uses partial application to constructcallbacks for Tkinter widgets on the fly:
fromTkinterimportTk,Canvas,Buttonimportsysfromfunctionalimportpartialwin=Tk()c=Canvas(win,width=200,height=50)c.pack()forcolourinsys.argv[1:]:b=Button(win,text=colour,command=partial(c.config,bg=colour))b.pack(side='left')win.mainloop()
I originally suggested the syntaxfn@(*args,**kw), meaning thesame aspartial(fn,*args,**kw).
The @ sign is used in some assembly languages to imply registerindirection, and the use here is also a kind of indirection.f@(x) is notf(x), but a thing that becomesf(x) when youcall it.
It was not well-received, so I have withdrawn this part of theproposal. In any case, @ has been taken for the new decorator syntax.
Among the opinions voiced were the following (which I summarise):
functional was well received, andthere are other things that belong there (for example functioncomposition).rightcurry) has been suggested.I agree that lambda is usually good enough, just not always. And Iwant the possibility of useful introspection and subclassing.
I disagree that @ is particularly ugly, but it may be that I’m justweird. We have dictionary, list and tuple literals neatlydifferentiated by special punctuation – a way of directly expressingpartially-applied function literals is not such a stretch. However,not one single person has said they like it, so as far as I’mconcerned it’s a dead parrot.
I concur with calling the class partial rather than curry or closure,so I have amended the proposal in this PEP accordingly. But notthroughout: some incorrect references to ‘curry’ have been left insince that’s where the discussion was at the time.
Partially applying arguments from the right, or inserting arguments atarbitrary positions creates its own problems, but pending discovery ofa good implementation and non-confusing semantics, I don’t think itshould be ruled out.
Carl Banks posted an implementation as a real functional closure:
defcurry(fn,*cargs,**ckwargs):defcall_fn(*fargs,**fkwargs):d=ckwargs.copy()d.update(fkwargs)returnfn(*(cargs+fargs),**d)returncall_fn
which he assures me is more efficient.
I also coded the class in Pyrex, to estimate how the performancemight be improved by coding it in C:
cdefclasscurry:cdefobjectfn,args,kwdef__init__(self,fn,*args,**kw):self.fn=fnself.args=argsself.kw=kwdef__call__(self,*args,**kw):ifself.kw:# from Python Cookbook versiond=self.kw.copy()d.update(kw)else:d=kwreturnself.fn(*(self.args+args),**d)
The performance gain in Pyrex is less than 100% over the nestedfunction implementation, since to be fully general it has to operateby Python API calls. For the same reason, a C implementation will beunlikely to be much faster, so the case for a built-in coded in C isnot very strong.
I prefer that some means to partially-apply functions and othercallables should be present in the standard library.
A standard library modulefunctional should contain animplementation ofpartial, and any other higher-order functionsthe community want. Other functions that might belong there falloutside the scope of this PEP though.
Patches for the implementation, documentation and unit tests (SFpatches931005,931007, and931010 respectively) have beensubmitted but not yet checked in.
A C implementation by Hye-Shik Chang has also been submitted, althoughit is not expected to be included until after the Pythonimplementation has proven itself useful enough to be worth optimising.
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0309.rst
Last modified:2025-02-01 08:59:27 GMT