mirror of
https://github.com/python/cpython.git
synced 2025-08-22 09:45:06 +00:00
Issue #27137: align Python & C implementations of functools.partial
The pure Python fallback implementation of functools.partial now matches the behaviour of its accelerated C counterpart for subclassing, pickling and text representation purposes. Patch by Emanuel Barry and Serhiy Storchaka.
This commit is contained in:
parent
eddc4b7272
commit
457fc9a69e
4 changed files with 179 additions and 89 deletions
|
@ -21,6 +21,7 @@ from abc import get_cache_token
|
|||
from collections import namedtuple
|
||||
from types import MappingProxyType
|
||||
from weakref import WeakKeyDictionary
|
||||
from reprlib import recursive_repr
|
||||
try:
|
||||
from _thread import RLock
|
||||
except ImportError:
|
||||
|
@ -237,26 +238,83 @@ except ImportError:
|
|||
################################################################################
|
||||
|
||||
# Purely functional, no descriptor behaviour
|
||||
def partial(func, *args, **keywords):
|
||||
class partial:
|
||||
"""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
|
||||
del tmpkw
|
||||
func = func.func
|
||||
|
||||
def newfunc(*fargs, **fkeywords):
|
||||
newkeywords = keywords.copy()
|
||||
newkeywords.update(fkeywords)
|
||||
return func(*(args + fargs), **newkeywords)
|
||||
newfunc.func = func
|
||||
newfunc.args = args
|
||||
newfunc.keywords = keywords
|
||||
return newfunc
|
||||
__slots__ = "func", "args", "keywords", "__dict__", "__weakref__"
|
||||
|
||||
def __new__(*args, **keywords):
|
||||
if not args:
|
||||
raise TypeError("descriptor '__new__' of partial needs an argument")
|
||||
if len(args) < 2:
|
||||
raise TypeError("type 'partial' takes at least one argument")
|
||||
cls, func, *args = args
|
||||
if not callable(func):
|
||||
raise TypeError("the first argument must be callable")
|
||||
args = tuple(args)
|
||||
|
||||
if hasattr(func, "func"):
|
||||
args = func.args + args
|
||||
tmpkw = func.keywords.copy()
|
||||
tmpkw.update(keywords)
|
||||
keywords = tmpkw
|
||||
del tmpkw
|
||||
func = func.func
|
||||
|
||||
self = super(partial, cls).__new__(cls)
|
||||
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.keywords = keywords
|
||||
return self
|
||||
|
||||
def __call__(*args, **keywords):
|
||||
if not args:
|
||||
raise TypeError("descriptor '__call__' of partial needs an argument")
|
||||
self, *args = args
|
||||
newkeywords = self.keywords.copy()
|
||||
newkeywords.update(keywords)
|
||||
return self.func(*self.args, *args, **newkeywords)
|
||||
|
||||
@recursive_repr()
|
||||
def __repr__(self):
|
||||
qualname = type(self).__qualname__
|
||||
args = [repr(self.func)]
|
||||
args.extend(repr(x) for x in self.args)
|
||||
args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items())
|
||||
if type(self).__module__ == "functools":
|
||||
return f"functools.{qualname}({', '.join(args)})"
|
||||
return f"{qualname}({', '.join(args)})"
|
||||
|
||||
def __reduce__(self):
|
||||
return type(self), (self.func,), (self.func, self.args,
|
||||
self.keywords or None, self.__dict__ or None)
|
||||
|
||||
def __setstate__(self, state):
|
||||
if not isinstance(state, tuple):
|
||||
raise TypeError("argument to __setstate__ must be a tuple")
|
||||
if len(state) != 4:
|
||||
raise TypeError(f"expected 4 items in state, got {len(state)}")
|
||||
func, args, kwds, namespace = state
|
||||
if (not callable(func) or not isinstance(args, tuple) or
|
||||
(kwds is not None and not isinstance(kwds, dict)) or
|
||||
(namespace is not None and not isinstance(namespace, dict))):
|
||||
raise TypeError("invalid partial state")
|
||||
|
||||
args = tuple(args) # just in case it's a subclass
|
||||
if kwds is None:
|
||||
kwds = {}
|
||||
elif type(kwds) is not dict: # XXX does it need to be *exactly* dict?
|
||||
kwds = dict(kwds)
|
||||
if namespace is None:
|
||||
namespace = {}
|
||||
|
||||
self.__dict__ = namespace
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.keywords = kwds
|
||||
|
||||
try:
|
||||
from _functools import partial
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue