diff options
author | INADA Naoki <methane@users.noreply.github.com> | 2018-01-16 11:52:41 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-16 11:52:41 (GMT) |
commit | 378edee0a3b913d60653dc17dfe61d83405a8135 (patch) | |
tree | e91c9b9b446aa0ce076da778bde6c1da27ff5e23 /Objects | |
parent | b44c5169f64178d2ff2914187b315549e7ab0cb6 (diff) | |
download | cpython-378edee0a3b913d60653dc17dfe61d83405a8135.zip cpython-378edee0a3b913d60653dc17dfe61d83405a8135.tar.gz cpython-378edee0a3b913d60653dc17dfe61d83405a8135.tar.bz2 |
bpo-32544: Speed up hasattr() and getattr() (GH-5173)
AttributeError was raised always when attribute is not found.
This commit skip raising AttributeError when `tp_getattro` is `PyObject_GenericGetAttr`.
It makes hasattr() and getattr() about 4x faster when attribute is not found.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/object.c | 56 |
1 files changed, 50 insertions, 6 deletions
diff --git a/Objects/object.c b/Objects/object.c index a0d651d..62d7fbe 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -887,10 +887,41 @@ PyObject_GetAttr(PyObject *v, PyObject *name) return NULL; } +PyObject * +_PyObject_GetAttrWithoutError(PyObject *v, PyObject *name) +{ + PyTypeObject *tp = Py_TYPE(v); + PyObject *ret = NULL; + + if (!PyUnicode_Check(name)) { + PyErr_Format(PyExc_TypeError, + "attribute name must be string, not '%.200s'", + name->ob_type->tp_name); + return NULL; + } + + if (tp->tp_getattro == PyObject_GenericGetAttr) { + return _PyObject_GenericGetAttrWithDict(v, name, NULL, 1); + } + if (tp->tp_getattro != NULL) { + ret = (*tp->tp_getattro)(v, name); + } + else if (tp->tp_getattr != NULL) { + const char *name_str = PyUnicode_AsUTF8(name); + if (name_str == NULL) + return NULL; + ret = (*tp->tp_getattr)(v, (char *)name_str); + } + if (ret == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } + return ret; +} + int PyObject_HasAttr(PyObject *v, PyObject *name) { - PyObject *res = PyObject_GetAttr(v, name); + PyObject *res = _PyObject_GetAttrWithoutError(v, name); if (res != NULL) { Py_DECREF(res); return 1; @@ -1098,10 +1129,13 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) /* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */ PyObject * -_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict) +_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, + PyObject *dict, int suppress) { /* Make sure the logic of _PyObject_GetMethod is in sync with this method. + + When suppress=1, this function suppress AttributeError. */ PyTypeObject *tp = Py_TYPE(obj); @@ -1132,6 +1166,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict) f = descr->ob_type->tp_descr_get; if (f != NULL && PyDescr_IsData(descr)) { res = f(descr, obj, (PyObject *)obj->ob_type); + if (res == NULL && suppress && + PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } goto done; } } @@ -1171,6 +1209,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict) if (f != NULL) { res = f(descr, obj, (PyObject *)Py_TYPE(obj)); + if (res == NULL && suppress && + PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } goto done; } @@ -1180,9 +1222,11 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict) goto done; } - PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%U'", - tp->tp_name, name); + if (!suppress) { + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%U'", + tp->tp_name, name); + } done: Py_XDECREF(descr); Py_DECREF(name); @@ -1192,7 +1236,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict) PyObject * PyObject_GenericGetAttr(PyObject *obj, PyObject *name) { - return _PyObject_GenericGetAttrWithDict(obj, name, NULL); + return _PyObject_GenericGetAttrWithDict(obj, name, NULL, 0); } int |