From d074c7530b812a7b78c9a201c8a70f281ed3a36e Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sat, 13 Jan 2024 14:21:36 -0500 Subject: [PATCH] Refs #35102 -- Optimized Expression.identity used for equality and hashing. inspect.signature() is quite slow and produces the same object for each instance of the same class as they share their __init__ method which makes it a prime candidate for caching. Thanks Anthony Shaw for the report. --- django/db/models/expressions.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index c20de5995a3..9ee2d65c8af 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -14,7 +14,7 @@ from django.db.models import fields from django.db.models.constants import LOOKUP_SEP from django.db.models.query_utils import Q from django.utils.deconstruct import deconstructible -from django.utils.functional import cached_property +from django.utils.functional import cached_property, classproperty from django.utils.hashable import make_hashable @@ -485,13 +485,18 @@ class BaseExpression: class Expression(BaseExpression, Combinable): """An expression that can be combined with other expressions.""" + @classproperty + @functools.lru_cache(maxsize=128) + def _constructor_signature(cls): + return inspect.signature(cls.__init__) + @cached_property def identity(self): - constructor_signature = inspect.signature(self.__init__) args, kwargs = self._constructor_args - signature = constructor_signature.bind_partial(*args, **kwargs) + signature = self._constructor_signature.bind_partial(self, *args, **kwargs) signature.apply_defaults() - arguments = signature.arguments.items() + arguments = iter(signature.arguments.items()) + next(arguments) identity = [self.__class__] for arg, value in arguments: if isinstance(value, fields.Field):