From 52773350687c8d463d7fa64cfaf29a14d748823a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 5 Jul 2023 11:28:06 +0200 Subject: [PATCH 1/7] generate dispatch method only once --- Lib/functools.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/functools.py b/Lib/functools.py index 4d5e2709007843..d4e283ef081058 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -933,6 +933,7 @@ def __init__(self, func): self.dispatcher = singledispatch(func) self.func = func + self.attrname = None def register(self, cls, method=None): """generic_method.register(cls, func) -> func @@ -949,12 +950,19 @@ def _method(*args, **kwargs): _method.__isabstractmethod__ = self.__isabstractmethod__ _method.register = self.register update_wrapper(_method, self.func) + if obj is not None: + # we set the method directly to the instance + obj.__dict__[self.attrname] = _method return _method @property def __isabstractmethod__(self): return getattr(self.func, '__isabstractmethod__', False) + def __set_name__(self, owner, name): + if self.attrname is None: + self.attrname = name + ################################################################################ ### cached_property() - computed once per instance, cached as attribute From 5eb3d3b8281525e20302d859bdd45622211fdf8b Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 5 Jul 2023 13:27:44 +0200 Subject: [PATCH 2/7] fix whitespace --- Lib/functools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index d4e283ef081058..b843b2db036f84 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -960,8 +960,8 @@ def __isabstractmethod__(self): return getattr(self.func, '__isabstractmethod__', False) def __set_name__(self, owner, name): - if self.attrname is None: - self.attrname = name + if self.attrname is None: + self.attrname = name ################################################################################ From ad5bd221c924b7f5e450b565777e2cdc3ef8a3a1 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:28:59 +0000 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst diff --git a/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst b/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst new file mode 100644 index 00000000000000..6a27bfbcea619a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst @@ -0,0 +1 @@ +Improve performance of ``functools.singledispatchmethod``. From abd16282a29060e0b25d16df5a623a8c3e90ccc7 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 5 Jul 2023 20:20:31 +0200 Subject: [PATCH 4/7] cache dispath method on singledispatch instance --- Lib/functools.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index b843b2db036f84..f0acf93041b110 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -933,7 +933,7 @@ def __init__(self, func): self.dispatcher = singledispatch(func) self.func = func - self.attrname = None + self._dispatch_method = None def register(self, cls, method=None): """generic_method.register(cls, func) -> func @@ -943,6 +943,9 @@ def register(self, cls, method=None): return self.dispatcher.register(cls, func=method) def __get__(self, obj, cls=None): + if self._dispatch_method: + return self._dispatch_method + def _method(*args, **kwargs): method = self.dispatcher.dispatch(args[0].__class__) return method.__get__(obj, cls)(*args, **kwargs) @@ -950,18 +953,12 @@ def _method(*args, **kwargs): _method.__isabstractmethod__ = self.__isabstractmethod__ _method.register = self.register update_wrapper(_method, self.func) - if obj is not None: - # we set the method directly to the instance - obj.__dict__[self.attrname] = _method return _method @property def __isabstractmethod__(self): return getattr(self.func, '__isabstractmethod__', False) - def __set_name__(self, owner, name): - if self.attrname is None: - self.attrname = name ################################################################################ From 4e068d4d0d7d1edfd2f2c3cd05e17a35b99985bb Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 5 Jul 2023 20:20:37 +0200 Subject: [PATCH 5/7] add test for slotted classes --- Lib/test/test_functools.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index d668fa4c3adf5c..263485d1e5934e 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -2474,6 +2474,26 @@ def _(arg): self.assertTrue(A.t('')) self.assertEqual(A.t(0.0), 0.0) + def test_staticmethod__slotted_class(self): + class A: + __slots__ = ['a'] + @functools.singledispatchmethod + def t(arg): + return arg + @t.register(int) + @staticmethod + def _(arg): + return isinstance(arg, int) + @t.register(str) + @staticmethod + def _(arg): + return isinstance(arg, str) + a = A() + + self.assertTrue(A.t(0)) + self.assertTrue(A.t('')) + self.assertEqual(A.t(0.0), 0.0) + def test_classmethod_register(self): class A: def __init__(self, arg): From 802f3d2e777a7a637514eee0483d04320dd5fccc Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 5 Jul 2023 23:16:03 +0200 Subject: [PATCH 6/7] add missing statement --- Lib/functools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/functools.py b/Lib/functools.py index f0acf93041b110..270513c432ddb3 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -953,6 +953,7 @@ def _method(*args, **kwargs): _method.__isabstractmethod__ = self.__isabstractmethod__ _method.register = self.register update_wrapper(_method, self.func) + self._dispatch_method = _method return _method @property From 72c8b0f90daa32dbcd37fa410a881b7a61b69e27 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 8 Jul 2023 19:35:01 +0200 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Dong-hee Na --- Lib/functools.py | 1 - .../next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/functools.py b/Lib/functools.py index 270513c432ddb3..7bb1f173b44786 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -961,7 +961,6 @@ def __isabstractmethod__(self): return getattr(self.func, '__isabstractmethod__', False) - ################################################################################ ### cached_property() - computed once per instance, cached as attribute ################################################################################ diff --git a/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst b/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst index 6a27bfbcea619a..6461c9fd4edaa5 100644 --- a/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst +++ b/Misc/NEWS.d/next/Library/2023-07-05-11-28-57.gh-issue-85160.t925jm.rst @@ -1 +1 @@ -Improve performance of ``functools.singledispatchmethod``. +Improve performance of :class:`functools.singledispatchmethod`.