From 0dbb4fba4c59741466ac18eeb946ca56989717d4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 20 Apr 2001 16:50:40 +0000 Subject: Implement, test and document "key in dict" and "key not in dict". I know some people don't like this -- if it's really controversial, I'll take it out again. (If it's only Alex Martelli who doesn't like it, that doesn't count as "real controversial" though. :-) That's why this is a separate checkin from the iterators stuff I'm about to check in next. --- Doc/lib/libstdtypes.tex | 12 +++++++++--- Doc/ref/ref3.tex | 11 ++++++++--- Doc/ref/ref5.tex | 4 +++- Lib/test/test_types.py | 4 ++++ Objects/dictobject.c | 36 +++++++++++++++++++++++++++++++++++- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Doc/lib/libstdtypes.tex b/Doc/lib/libstdtypes.tex index 79221b8..f6786a1 100644 --- a/Doc/lib/libstdtypes.tex +++ b/Doc/lib/libstdtypes.tex @@ -867,9 +867,15 @@ arbitrary objects): {(1)} \lineiii{\var{a}.clear()}{remove all items from \code{a}}{} \lineiii{\var{a}.copy()}{a (shallow) copy of \code{a}}{} - \lineiii{\var{a}.has_key(\var{k})} + \lineiii{\var{k} \code{in} \var{a}} {\code{1} if \var{a} has a key \var{k}, else \code{0}} {} + \lineiii{\var{k} not in \var{a}} + {\code{0} if \var{a} has a key \var{k}, else \code{1}} + {} + \lineiii{\var{a}.has_key(\var{k})} + {Equivalent to \var{k} \code{in} \var{a}} + {} \lineiii{\var{a}.items()} {a copy of \var{a}'s list of (\var{key}, \var{value}) pairs} {(2)} @@ -879,11 +885,11 @@ arbitrary objects): {(3)} \lineiii{\var{a}.values()}{a copy of \var{a}'s list of values}{(2)} \lineiii{\var{a}.get(\var{k}\optional{, \var{x}})} - {\code{\var{a}[\var{k}]} if \code{\var{a}.has_key(\var{k})}, + {\code{\var{a}[\var{k}]} if \code{\var{k} in \var{a}}}, else \var{x}} {(4)} \lineiii{\var{a}.setdefault(\var{k}\optional{, \var{x}})} - {\code{\var{a}[\var{k}]} if \code{\var{a}.has_key(\var{k})}, + {\code{\var{a}[\var{k}]} if \code{\var{k} in \var{a}}}, else \var{x} (also setting it)} {(5)} \lineiii{\var{a}.popitem()} diff --git a/Doc/ref/ref3.tex b/Doc/ref/ref3.tex index 1f3afbf..adb619a 100644 --- a/Doc/ref/ref3.tex +++ b/Doc/ref/ref3.tex @@ -1134,7 +1134,10 @@ multiplication (meaning repetition) by defining the methods \method{__add__()}, \method{__radd__()}, \method{__iadd__()}, \method{__mul__()}, \method{__rmul__()} and \method{__imul__()} described below; they should not define \method{__coerce__()} or other numerical -operators. +operators. It is recommended that both mappings and sequences +implement the \method{__contains__}, to allow efficient use of the +\code{in} operator; for mappings, \code{in} should be equivalent of +\method{has_key()}; for sequences, it should search through the values. \withsubitem{(mapping object method)}{ \ttindex{keys()} \ttindex{values()} @@ -1143,7 +1146,8 @@ operators. \ttindex{get()} \ttindex{clear()} \ttindex{copy()} - \ttindex{update()}} + \ttindex{update()} + \ttindex{__contains__()}} \withsubitem{(sequence object method)}{ \ttindex{append()} \ttindex{count()} @@ -1158,7 +1162,8 @@ operators. \ttindex{__iadd__()} \ttindex{__mul__()} \ttindex{__rmul__()} - \ttindex{__imul__()}} + \ttindex{__imul__()} + \ttindex{__contains__()}} \withsubitem{(numeric object method)}{\ttindex{__coerce__()}} \begin{methoddesc}[mapping object]{__len__}{self} diff --git a/Doc/ref/ref5.tex b/Doc/ref/ref5.tex index 5663daa..ec0c6b1 100644 --- a/Doc/ref/ref5.tex +++ b/Doc/ref/ref5.tex @@ -768,7 +768,9 @@ not in \var{s}} returns the negation of \code{\var{x} in \var{s}}. The set membership test has traditionally been bound to sequences; an object is a member of a set if the set is a sequence and contains an element equal to that object. However, it is possible for an object -to support membership tests without being a sequence. +to support membership tests without being a sequence. In particular, +dictionaries support memership testing as a nicer way of spelling +\code{\var{key} in \var{dict}}; other mapping types may follow suit. For the list and tuple types, \code{\var{x} in \var{y}} is true if and only if there exists an index \var{i} such that diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index bf35687..9df3955 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -221,6 +221,8 @@ print '6.6 Mappings == Dictionaries' d = {} if d.keys() != []: raise TestFailed, '{}.keys()' if d.has_key('a') != 0: raise TestFailed, '{}.has_key(\'a\')' +if ('a' in d) != 0: raise TestFailed, "'a' in {}" +if ('a' not in d) != 1: raise TestFailed, "'a' not in {}" if len(d) != 0: raise TestFailed, 'len({})' d = {'a': 1, 'b': 2} if len(d) != 2: raise TestFailed, 'len(dict)' @@ -229,6 +231,8 @@ k.sort() if k != ['a', 'b']: raise TestFailed, 'dict keys()' if d.has_key('a') and d.has_key('b') and not d.has_key('c'): pass else: raise TestFailed, 'dict keys()' +if 'a' in d and 'b' in d and 'c' not in d: pass +else: raise TestFailed, 'dict keys() # in/not in version' if d['a'] != 1 or d['b'] != 2: raise TestFailed, 'dict item' d['c'] = 3 d['a'] = 4 diff --git a/Objects/dictobject.c b/Objects/dictobject.c index cc10db0..d00e326 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1292,6 +1292,40 @@ dict_getattr(dictobject *mp, char *name) return Py_FindMethod(mapp_methods, (PyObject *)mp, name); } +static int +dict_contains(dictobject *mp, PyObject *key) +{ + long hash; + +#ifdef CACHE_HASH + if (!PyString_Check(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) +#endif + { + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + } + return (mp->ma_size != 0 + && (mp->ma_lookup)(mp, key, hash)->me_value != NULL); +} + +staticforward PyObject *dictiter_new(dictobject *); + +/* Hack to implement "key in dict" */ +static PySequenceMethods dict_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)dict_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + PyTypeObject PyDict_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, @@ -1305,7 +1339,7 @@ PyTypeObject PyDict_Type = { (cmpfunc)dict_compare, /* tp_compare */ (reprfunc)dict_repr, /* tp_repr */ 0, /* tp_as_number */ - 0, /* tp_as_sequence */ + &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ -- cgit v0.12