Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 309 – Partial Function Application

Author:
Peter Harris <scav at blueyonder.co.uk>
Status:
Final
Type:
Standards Track
Created:
08-Feb-2003
Python-Version:
2.5
Post-History:
10-Feb-2003, 27-Feb-2003, 22-Feb-2004, 28-Apr-2006

Table of Contents

Note

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.

Abstract

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].

Acceptance

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.

Motivation

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.

Example Implementation

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.

Examples of Use

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()

Abandoned Syntax Proposal

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.

Feedback from comp.lang.python and python-dev

Among the opinions voiced were the following (which I summarise):

  • Lambda is good enough.
  • The @ syntax is ugly (unanimous).
  • It’s really a curry rather than a closure. There is an almostidentical implementation of a curry class on ActiveState’s PythonCookbook.
  • A curry class would indeed be a useful addition to the standardlibrary.
  • It isn’t function currying, but partial application. Hence thename is now proposed to be partial().
  • It maybe isn’t useful enough to be in the built-ins.
  • The idea of a module calledfunctional was well received, andthere are other things that belong there (for example functioncomposition).
  • For completeness, another object that appends partial argumentsafter those supplied in the function call (maybe calledrightcurry) 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.

Summary

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.

References

[1]
https://mail.python.org/pipermail/python-dev/2006-March/062290.html
[2]
Patches931005,931007, and931010.
[3]
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549

Copyright

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


[8]ページ先頭

©2009-2025 Movatter.jp