diff -r 2c4448bbed1f Doc/library/functools.rst --- a/Doc/library/functools.rst Thu Feb 25 01:35:21 2016 +1100 +++ b/Doc/library/functools.rst Thu Feb 25 10:15:49 2016 +0200 @@ -164,22 +164,22 @@ The :mod:`functools` module defines the Returning NotImplemented from the underlying comparison function for unrecognised types is now supported. -.. function:: partial(func, *args, **keywords) +.. function:: partial(func, *args, **kwargs) Return a new :class:`partial` object which when called will behave like *func* - called with the positional arguments *args* and keyword arguments *keywords*. If + called with the positional arguments *args* and keyword arguments *kwargs*. If more arguments are supplied to the call, they are appended to *args*. If - additional keyword arguments are supplied, they extend and override *keywords*. + additional keyword arguments are supplied, they extend and override *kwargs*. Roughly equivalent to:: - def partial(func, *args, **keywords): - def newfunc(*fargs, **fkeywords): - newkeywords = keywords.copy() - newkeywords.update(fkeywords) - return func(*(args + fargs), **newkeywords) + def partial(func, *args, **kwargs): + def newfunc(*fargs, **fkwargs): + newkwargs = kwargs.copy() + newkwargs.update(fkwargs) + return func(*(args + fargs), **newkwargs) newfunc.func = func newfunc.args = args - newfunc.keywords = keywords + newfunc.kwargs = kwargs return newfunc The :func:`partial` is used for partial function application which "freezes" @@ -195,7 +195,7 @@ The :mod:`functools` module defines the 18 -.. class:: partialmethod(func, *args, **keywords) +.. class:: partialmethod(func, *args, **kwargs) Return a new :class:`partialmethod` descriptor which behaves like :class:`partial` except that it is designed to be used as a method @@ -213,7 +213,7 @@ The :mod:`functools` module defines the When *func* is a non-descriptor callable, an appropriate bound method is created dynamically. This behaves like a normal Python function when used as a method: the *self* argument will be inserted as the first - positional argument, even before the *args* and *keywords* supplied to + positional argument, even before the *args* and *kwargs* supplied to the :class:`partialmethod` constructor. Example:: @@ -451,7 +451,7 @@ The :mod:`functools` module defines the ------------------------ :class:`partial` objects are callable objects created by :func:`partial`. They -have three read-only attributes: +have following read-only attributes: .. attribute:: partial.func @@ -466,11 +466,15 @@ have three read-only attributes: arguments provided to a :class:`partial` object call. +.. attribute:: partial.kwargs .. attribute:: partial.keywords The keyword arguments that will be supplied when the :class:`partial` object is called. +.. versionchanged:: 3.6 + Added the :attr:`kwargs` attribute as an alias to the :attr:`keywords` attribute. + :class:`partial` objects are like :class:`function` objects in that they are callable, weak referencable, and can have attributes. There are some important differences. For instance, the :attr:`__name__` and :attr:`__doc__` attributes diff -r 2c4448bbed1f Lib/functools.py --- a/Lib/functools.py Thu Feb 25 01:35:21 2016 +1100 +++ b/Lib/functools.py Thu Feb 25 10:15:49 2016 +0200 @@ -237,25 +237,26 @@ except ImportError: ################################################################################ # Purely functional, no descriptor behaviour -def partial(func, *args, **keywords): +def partial(func, *args, **kwargs): """New function with partial application of the given arguments and keywords. """ if hasattr(func, 'func'): args = func.args + args tmpkw = func.keywords.copy() - tmpkw.update(keywords) - keywords = tmpkw + tmpkw.update(kwargs) + kwargs = tmpkw del tmpkw func = func.func - def newfunc(*fargs, **fkeywords): - newkeywords = keywords.copy() - newkeywords.update(fkeywords) - return func(*(args + fargs), **newkeywords) + def newfunc(*fargs, **fkwargs): + newkwargs = kwargs.copy() + newkwargs.update(fkwargs) + return func(*(args + fargs), **newkwargs) newfunc.func = func newfunc.args = args - newfunc.keywords = keywords + newfunc.kwargs = kwargs + newfunc.keywords = kwargs return newfunc try: @@ -272,7 +273,7 @@ class partialmethod(object): callables as instance methods. """ - def __init__(self, func, *args, **keywords): + def __init__(self, func, *args, **kwargs): if not callable(func) and not hasattr(func, "__get__"): raise TypeError("{!r} is not callable or a descriptor" .format(func)) @@ -290,7 +291,7 @@ class partialmethod(object): else: self.func = func self.args = args - self.keywords = keywords + self.keywords = kwargs def __repr__(self): args = ", ".join(map(repr, self.args)) @@ -304,12 +305,12 @@ class partialmethod(object): keywords=keywords) def _make_unbound_method(self): - def _method(*args, **keywords): - call_keywords = self.keywords.copy() - call_keywords.update(keywords) + def _method(*args, **kwargs): + call_kwargs = self.keywords.copy() + call_kwargs.update(kwargs) cls_or_self, *rest = args call_args = (cls_or_self,) + self.args + tuple(rest) - return self.func(*call_args, **call_keywords) + return self.func(*call_args, **call_kwargs) _method.__isabstractmethod__ = self.__isabstractmethod__ _method._partialmethod = self return _method @@ -337,6 +338,10 @@ class partialmethod(object): def __isabstractmethod__(self): return getattr(self.func, "__isabstractmethod__", False) + @property + def kwargs(self): + return self.keywords + ################################################################################ ### LRU Cache function decorator diff -r 2c4448bbed1f Lib/test/test_functools.py --- a/Lib/test/test_functools.py Thu Feb 25 01:35:21 2016 +1100 +++ b/Lib/test/test_functools.py Thu Feb 25 10:15:49 2016 +0200 @@ -28,7 +28,7 @@ def capture(*args, **kw): def signature(part): """ return the signature of a partial object """ - return (part.func, part.args, part.keywords, part.__dict__) + return (part.func, part.args, part.kwargs, part.__dict__) class MyTuple(tuple): pass @@ -56,6 +56,7 @@ class TestPartial: # attributes should be readable self.assertEqual(p.func, capture) self.assertEqual(p.args, (1, 2)) + self.assertEqual(p.kwargs, dict(a=10, b=20)) self.assertEqual(p.keywords, dict(a=10, b=20)) def test_argument_checking(self): @@ -92,10 +93,12 @@ class TestPartial: # exercise special code paths for no keyword args in # either the partial object or the caller p = self.partial(capture) + self.assertEqual(p.kwargs, {}) self.assertEqual(p.keywords, {}) self.assertEqual(p(), ((), {})) self.assertEqual(p(a=1), ((), {'a':1})) p = self.partial(capture, a=1) + self.assertEqual(p.kwargs, {'a':1}) self.assertEqual(p.keywords, {'a':1}) self.assertEqual(p(), ((), {'a':1})) self.assertEqual(p(b=2), ((), {'a':1, 'b':2})) @@ -231,6 +234,7 @@ class TestPartialC(TestPartial, unittest self.assertEqual(signature(f_copy), signature(f)) self.assertIs(f_copy.attr, f.attr) self.assertIs(f_copy.args, f.args) + self.assertIs(f_copy.kwargs, f.kwargs) self.assertIs(f_copy.keywords, f.keywords) def test_deepcopy(self): @@ -241,7 +245,9 @@ class TestPartialC(TestPartial, unittest self.assertIsNot(f_copy.attr, f.attr) self.assertIsNot(f_copy.args, f.args) self.assertIsNot(f_copy.args[0], f.args[0]) + self.assertIsNot(f_copy.kwargs, f.kwargs) self.assertIsNot(f_copy.keywords, f.keywords) + self.assertIsNot(f_copy.kwargs['bar'], f.kwargs['bar']) self.assertIsNot(f_copy.keywords['bar'], f.keywords['bar']) def test_setstate(self): diff -r 2c4448bbed1f Modules/_functoolsmodule.c --- a/Modules/_functoolsmodule.c Thu Feb 25 01:35:21 2016 +1100 +++ b/Modules/_functoolsmodule.c Thu Feb 25 10:15:49 2016 +0200 @@ -181,7 +181,7 @@ partial_traverse(partialobject *pto, vis } PyDoc_STRVAR(partial_doc, -"partial(func, *args, **keywords) - new function with partial application\n\ +"partial(func, *args, **kwargs) - new function with partial application\n\ of the given arguments and keywords.\n"); #define OFF(x) offsetof(partialobject, x) @@ -190,8 +190,10 @@ static PyMemberDef partial_memberlist[] "function object to use in future partial calls"}, {"args", T_OBJECT, OFF(args), READONLY, "tuple of arguments to future partial calls"}, + {"kwargs", T_OBJECT, OFF(kw), READONLY, + "dictionary of keyword arguments to future partial calls"}, {"keywords", T_OBJECT, OFF(kw), READONLY, - "dictionary of keyword arguments to future partial calls"}, + "an alias to kwargs"}, {NULL} /* Sentinel */ };