summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwangxiang-hz <34048878+wangxiang-hz@users.noreply.github.com>2023-03-11 11:11:37 (GMT)
committerGitHub <noreply@github.com>2023-03-11 11:11:37 (GMT)
commitaa0a73d1bc53dcb6348a869df1e775138991e561 (patch)
tree66ad74f8d199076d88e5150b999895cd15807560
parent5ffdaf748d98da6065158534720f1996a45a0072 (diff)
downloadcpython-aa0a73d1bc53dcb6348a869df1e775138991e561.zip
cpython-aa0a73d1bc53dcb6348a869df1e775138991e561.tar.gz
cpython-aa0a73d1bc53dcb6348a869df1e775138991e561.tar.bz2
gh-102213: Optimize the performance of `__getattr__` (GH-102248)
When __getattr__ is defined, python with try to find an attribute using _PyObject_GenericGetAttrWithDict find nothing is reasonable so we don't need an exception, it will hurt performance.
-rw-r--r--Include/internal/pycore_object.h1
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-02-26-13-12-55.gh-issue-102213.fTH8X7.rst1
-rw-r--r--Objects/object.c6
-rw-r--r--Objects/typeobject.c9
4 files changed, 14 insertions, 3 deletions
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index e15685f..318e6f3 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -370,6 +370,7 @@ extern void _PyObject_FreeInstanceAttributes(PyObject *obj);
extern int _PyObject_IsInstanceDictEmpty(PyObject *);
extern int _PyType_HasSubclasses(PyTypeObject *);
extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
+extern PyObject* _PyObject_GenericTryGetAttr(PyObject *, PyObject *);
// Access macro to the members which are floating "behind" the object
static inline PyMemberDef* _PyHeapType_GET_MEMBERS(PyHeapTypeObject *etype) {
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-26-13-12-55.gh-issue-102213.fTH8X7.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-26-13-12-55.gh-issue-102213.fTH8X7.rst
new file mode 100644
index 0000000..997bef2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-26-13-12-55.gh-issue-102213.fTH8X7.rst
@@ -0,0 +1 @@
+Fix performance loss when accessing an object's attributes with ``__getattr__`` defined.
diff --git a/Objects/object.c b/Objects/object.c
index 38da4d4..dff5e2a 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1405,6 +1405,12 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
return _PyObject_GenericGetAttrWithDict(obj, name, NULL, 0);
}
+PyObject *
+_PyObject_GenericTryGetAttr(PyObject *obj, PyObject *name)
+{
+ return _PyObject_GenericGetAttrWithDict(obj, name, NULL, 1);
+}
+
int
_PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
PyObject *value, PyObject *dict)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index f486b83..f0654c2 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -8247,14 +8247,17 @@ _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
(Py_IS_TYPE(getattribute, &PyWrapperDescr_Type) &&
((PyWrapperDescrObject *)getattribute)->d_wrapped ==
(void *)PyObject_GenericGetAttr))
- res = PyObject_GenericGetAttr(self, name);
+ /* finding nothing is reasonable when __getattr__ is defined */
+ res = _PyObject_GenericTryGetAttr(self, name);
else {
Py_INCREF(getattribute);
res = call_attribute(self, getattribute, name);
Py_DECREF(getattribute);
}
- if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
- PyErr_Clear();
+ if (res == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ }
res = call_attribute(self, getattr, name);
}
Py_DECREF(getattr);