Skip to content

Commit 2654668

Browse files
author
rhettinger
committed
* Added a new method flag, METH_COEXIST.
* Used the flag to optimize set.__contains__(), dict.__contains__(), dict.__getitem__(), and list.__getitem__(). git-svn-id: http://svn.python.org/projects/python/trunk@34896 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 3e472e1 commit 2654668

8 files changed

Lines changed: 97 additions & 3 deletions

File tree

Doc/api/newtypes.tex

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,21 @@ \section{Common Object Structures \label{common-structs}}
306306
\versionadded{2.3}
307307
\end{datadesc}
308308

309+
One other constant controls whether a method is loaded in place of
310+
another definition with the same method name.
311+
312+
\begin{datadesc}{METH_COEXIST}
313+
The method will be loaded in place of existing definitions. Without
314+
\var{METH_COEXIST}, the default is to skip repeated definitions. Since
315+
slot wrappers are loaded before the method table, the existence of a
316+
\var{sq_contains} slot, for example, would generate a wrapped method
317+
named \method{__contains__()} and preclude the loading of a
318+
corresponding PyCFunction with the same name. With the flag defined,
319+
the PyCFunction will be loaded in place of the wrapper object and will
320+
co-exist with the slot. This is helpful because calls to PyCFunctions
321+
are optimized more than wrapper object calls.
322+
\versionadded{2.4}
323+
\end{datadesc}
309324

310325
\begin{cfuncdesc}{PyObject*}{Py_FindMethod}{PyMethodDef table[],
311326
PyObject *ob, char *name}

Include/methodobject.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *,
5858
#define METH_CLASS 0x0010
5959
#define METH_STATIC 0x0020
6060

61+
/* METH_COEXIST allows a method to be entered eventhough a slot has
62+
already filled the entry. When defined, the flag allows a separate
63+
method, "__contains__" for example, to coexist with a defined
64+
slot like sq_contains. */
65+
66+
#define METH_COEXIST 0x0040
67+
6168
typedef struct PyMethodChain {
6269
PyMethodDef *methods; /* Methods of this type */
6370
struct PyMethodChain *link; /* NULL or base type */

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ Build
281281
C API
282282
-----
283283

284+
- Created a new method flag, METH_COEXIST, which causes a method to be loaded
285+
even if already defined by a slot wrapper. This allows a __contains__
286+
method, for example, to co-exist with a defined sq_contains slot. This
287+
is helpful because the PyCFunction can take advantage of optimized calls
288+
whenever METH_O or METH_NOARGS flags are defined.
289+
284290
- Added a new function, PyDict_Contains(d, k) which is like
285291
PySequence_Contains() but is specific to dictionaries and executes
286292
about 10% faster.

Objects/dictobject.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,31 @@ PyDict_GetItem(PyObject *op, PyObject *key)
498498
return (mp->ma_lookup)(mp, key, hash)->me_value;
499499
}
500500

501+
static PyObject *
502+
dict_getitem(PyObject *op, PyObject *key)
503+
{
504+
long hash;
505+
dictobject *mp = (dictobject *)op;
506+
PyObject *v;
507+
508+
if (!PyDict_Check(op)) {
509+
return NULL;
510+
}
511+
if (!PyString_CheckExact(key) ||
512+
(hash = ((PyStringObject *) key)->ob_shash) == -1)
513+
{
514+
hash = PyObject_Hash(key);
515+
if (hash == -1)
516+
return NULL;
517+
}
518+
v = (mp->ma_lookup)(mp, key, hash) -> me_value;
519+
if (v == NULL)
520+
PyErr_SetObject(PyExc_KeyError, key);
521+
else
522+
Py_INCREF(v);
523+
return v;
524+
}
525+
501526
/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
502527
* dictionary if it is merely replacing the value for an existing key.
503528
* This is means that it's safe to loop over a dictionary with
@@ -1735,6 +1760,11 @@ dict_iteritems(dictobject *dict)
17351760
PyDoc_STRVAR(has_key__doc__,
17361761
"D.has_key(k) -> True if D has a key k, else False");
17371762

1763+
PyDoc_STRVAR(contains__doc__,
1764+
"D.__contains__(k) -> True if D has a key k, else False");
1765+
1766+
PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]");
1767+
17381768
PyDoc_STRVAR(get__doc__,
17391769
"D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.");
17401770

@@ -1781,6 +1811,10 @@ PyDoc_STRVAR(iteritems__doc__,
17811811
"D.iteritems() -> an iterator over the (key, value) items of D");
17821812

17831813
static PyMethodDef mapp_methods[] = {
1814+
{"__contains__",(PyCFunction)dict_has_key, METH_O | METH_COEXIST,
1815+
contains__doc__},
1816+
{"__getitem__", (PyCFunction)dict_getitem, METH_O | METH_COEXIST,
1817+
getitem__doc__},
17841818
{"has_key", (PyCFunction)dict_has_key, METH_O,
17851819
has_key__doc__},
17861820
{"get", (PyCFunction)dict_get, METH_VARARGS,

Objects/listobject.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,6 +2371,8 @@ list_nohash(PyObject *self)
23712371
static PyObject *list_iter(PyObject *seq);
23722372
static PyObject *list_reversed(PyListObject* seq, PyObject* unused);
23732373

2374+
PyDoc_STRVAR(getitem_doc,
2375+
"x.__getitem__(y) <==> x[y]");
23742376
PyDoc_STRVAR(reversed_doc,
23752377
"L.__reversed__() -- return a reverse iterator over the list");
23762378
PyDoc_STRVAR(append_doc,
@@ -2396,7 +2398,10 @@ PyDoc_STRVAR(sorted_doc,
23962398
"list.sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list;\n\
23972399
cmp(x, y) -> -1, 0, 1");
23982400

2401+
static PyObject *list_subscript(PyListObject*, PyObject*);
2402+
23992403
static PyMethodDef list_methods[] = {
2404+
{"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, getitem_doc},
24002405
{"__reversed__",(PyCFunction)list_reversed, METH_NOARGS, reversed_doc},
24012406
{"append", (PyCFunction)listappend, METH_O, append_doc},
24022407
{"insert", (PyCFunction)listinsert, METH_VARARGS, insert_doc},

Objects/methodobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw)
6767
PyObject *self = PyCFunction_GET_SELF(func);
6868
int size;
6969

70-
switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC)) {
70+
switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) {
7171
case METH_VARARGS:
7272
if (kw == NULL || PyDict_Size(kw) == 0)
7373
return (*meth)(self, arg);

Objects/setobject.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,28 @@ set_contains(PySetObject *so, PyObject *key)
155155
return result;
156156
}
157157

158+
static PyObject *
159+
set_direct_contains(PySetObject *so, PyObject *key)
160+
{
161+
PyObject *tmp;
162+
long result;
163+
164+
result = PyDict_Contains(so->data, key);
165+
if (result == -1 && PyAnySet_Check(key)) {
166+
PyErr_Clear();
167+
tmp = frozenset_dict_wrapper(((PySetObject *)(key))->data);
168+
if (tmp == NULL)
169+
return NULL;
170+
result = PyDict_Contains(so->data, tmp);
171+
Py_DECREF(tmp);
172+
}
173+
if (result == -1)
174+
return NULL;
175+
return PyBool_FromLong(result);
176+
}
177+
178+
PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x.");
179+
158180
static PyObject *
159181
set_copy(PySetObject *so)
160182
{
@@ -968,6 +990,8 @@ static PyMethodDef set_methods[] = {
968990
add_doc},
969991
{"clear", (PyCFunction)set_clear, METH_NOARGS,
970992
clear_doc},
993+
{"__contains__", (PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
994+
contains_doc},
971995
{"copy", (PyCFunction)set_copy, METH_NOARGS,
972996
copy_doc},
973997
{"__copy__", (PyCFunction)set_copy, METH_NOARGS,
@@ -1094,6 +1118,8 @@ PyTypeObject PySet_Type = {
10941118

10951119

10961120
static PyMethodDef frozenset_methods[] = {
1121+
{"__contains__", (PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
1122+
contains_doc},
10971123
{"copy", (PyCFunction)frozenset_copy, METH_NOARGS,
10981124
copy_doc},
10991125
{"__copy__", (PyCFunction)frozenset_copy, METH_NOARGS,

Objects/typeobject.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,8 +2792,9 @@ add_methods(PyTypeObject *type, PyMethodDef *meth)
27922792

27932793
for (; meth->ml_name != NULL; meth++) {
27942794
PyObject *descr;
2795-
if (PyDict_GetItemString(dict, meth->ml_name))
2796-
continue;
2795+
if (PyDict_GetItemString(dict, meth->ml_name) &&
2796+
!(meth->ml_flags & METH_COEXIST))
2797+
continue;
27972798
if (meth->ml_flags & METH_CLASS) {
27982799
if (meth->ml_flags & METH_STATIC) {
27992800
PyErr_SetString(PyExc_ValueError,

0 commit comments

Comments
 (0)