Skip to content

Commit

Permalink
gh-102213: Optimize the performance of __getattr__ (GH-102248)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wangxiang-hz authored Mar 11, 2023
1 parent 5ffdaf7 commit aa0a73d
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 3 deletions.
1 change: 1 addition & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix performance loss when accessing an object's attributes with ``__getattr__`` defined.
6 changes: 6 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 6 additions & 3 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

This comment has been minimized.

Copy link
@sunmy2019

sunmy2019 Apr 23, 2023

Member

did not reraise exception in this path

This comment has been minimized.

Copy link
@Fidget-Spinner

Fidget-Spinner Apr 24, 2023

Member

Do you mind re-opening a PR please?

}
Py_DECREF(getattr);
Expand Down

0 comments on commit aa0a73d

Please sign in to comment.