From 218f05e3d6a74c37e2464b9a49b7dbe560b53291 Mon Sep 17 00:00:00 2001 From: "d.grigonis" Date: Wed, 12 Jun 2024 04:04:30 +0300 Subject: [PATCH] in_ and not_in --- Doc/library/operator.rst | 10 +++++ Lib/operator.py | 15 +++++++- Lib/test/test_operator.py | 16 ++++++++ Modules/_operator.c | 40 ++++++++++++++++++++ Modules/clinic/_operator.c.h | 72 +++++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 2 deletions(-) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index a9a6026af406fe8..e43fcc515345851 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -208,6 +208,16 @@ Operations which work with sequences (some of them with mappings too) include: Return ``a + b`` for *a* and *b* sequences. +.. function:: in_(a, b) + + Return the outcome of the test ``a in b``. + + +.. function:: not_in(a, b) + + Return the outcome of the test ``a not in b``. + + .. function:: contains(a, b) __contains__(a, b) diff --git a/Lib/operator.py b/Lib/operator.py index 02ccdaa13ddb318..7c36d650f430f5e 100644 --- a/Lib/operator.py +++ b/Lib/operator.py @@ -10,7 +10,8 @@ This is the pure Python implementation of the module. """ -__all__ = ['abs', 'add', 'and_', 'attrgetter', 'call', 'concat', 'contains', 'countOf', +__all__ = ['abs', 'add', 'and_', 'attrgetter', 'call', 'concat', + 'in_', 'not_in', 'contains', 'countOf', 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand', 'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul', 'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', @@ -150,10 +151,22 @@ def concat(a, b): raise TypeError(msg) return a + b + +def in_(a, b): + "Same as a in b." + return a in b + + +def not_in(a, b): + "Same as a not in b." + return a not in b + + def contains(a, b): "Same as b in a (note reversed operands)." return b in a + def countOf(a, b): "Return the number of items in a which are, or which equal, b." count = 0 diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index f8eac8dc0026369..e8cdd0178d57583 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -290,6 +290,22 @@ def test_rshift(self): self.assertEqual(operator.rshift(5, 0), 5) self.assertRaises(ValueError, operator.rshift, 2, -1) + def test_in(self): + operator = self.module + self.assertRaises(TypeError, operator.in_) + self.assertRaises(TypeError, operator.in_, None, None) + self.assertRaises(ZeroDivisionError, operator.in_, 1, BadIterable()) + self.assertTrue(operator.in_(2, range(4))) + self.assertFalse(operator.in_(5, range(4))) + + def test_not_in(self): + operator = self.module + self.assertRaises(TypeError, operator.not_in) + self.assertRaises(TypeError, operator.not_in, None, None) + self.assertRaises(ZeroDivisionError, operator.not_in, 1, BadIterable()) + self.assertFalse(operator.not_in(2, range(4))) + self.assertTrue(operator.not_in(5, range(4))) + def test_contains(self): operator = self.module self.assertRaises(TypeError, operator.contains) diff --git a/Modules/_operator.c b/Modules/_operator.c index 5d3f88327d19ad7..3e038c7916c7d00 100644 --- a/Modules/_operator.c +++ b/Modules/_operator.c @@ -474,6 +474,44 @@ _operator_iconcat_impl(PyObject *module, PyObject *a, PyObject *b) return PySequence_InPlaceConcat(a, b); } +/*[clinic input] +_operator.in_ -> bool + + a: object + b: object + / + +Same as a in b. +[clinic start generated code]*/ + +static int +_operator_in__impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=cd4eca456096fa9a input=8bd20ace23fe9808]*/ +{ + return PySequence_Contains(b, a); +} + +/*[clinic input] +_operator.not_in -> bool + + a: object + b: object + / + +Same as a not in b. +[clinic start generated code]*/ + +static int +_operator_not_in_impl(PyObject *module, PyObject *a, PyObject *b) +/*[clinic end generated code: output=93a4db26908835bd input=203499d558a29ee3]*/ +{ + int result = PySequence_Contains(b, a); + if (result != -1) { + result = 1 - result; + } + return result; +} + /*[clinic input] _operator.contains -> bool @@ -910,6 +948,8 @@ _operator_call(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje static struct PyMethodDef operator_methods[] = { _OPERATOR_TRUTH_METHODDEF + _OPERATOR_IN__METHODDEF + _OPERATOR_NOT_IN_METHODDEF _OPERATOR_CONTAINS_METHODDEF _OPERATOR_INDEXOF_METHODDEF _OPERATOR_COUNTOF_METHODDEF diff --git a/Modules/clinic/_operator.c.h b/Modules/clinic/_operator.c.h index 08615d690922a1c..2351fe330a77253 100644 --- a/Modules/clinic/_operator.c.h +++ b/Modules/clinic/_operator.c.h @@ -886,6 +886,76 @@ _operator_iconcat(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } +PyDoc_STRVAR(_operator_in___doc__, +"in_($module, a, b, /)\n" +"--\n" +"\n" +"Same as a in b."); + +#define _OPERATOR_IN__METHODDEF \ + {"in_", _PyCFunction_CAST(_operator_in_), METH_FASTCALL, _operator_in___doc__}, + +static int +_operator_in__impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +_operator_in_(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + int _return_value; + + if (!_PyArg_CheckPositional("in_", nargs, 2, 2)) { + goto exit; + } + a = args[0]; + b = args[1]; + _return_value = _operator_in__impl(module, a, b); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + +PyDoc_STRVAR(_operator_not_in__doc__, +"not_in($module, a, b, /)\n" +"--\n" +"\n" +"Same as a not in b."); + +#define _OPERATOR_NOT_IN_METHODDEF \ + {"not_in", _PyCFunction_CAST(_operator_not_in), METH_FASTCALL, _operator_not_in__doc__}, + +static int +_operator_not_in_impl(PyObject *module, PyObject *a, PyObject *b); + +static PyObject * +_operator_not_in(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + PyObject *a; + PyObject *b; + int _return_value; + + if (!_PyArg_CheckPositional("not_in", nargs, 2, 2)) { + goto exit; + } + a = args[0]; + b = args[1]; + _return_value = _operator_not_in_impl(module, a, b); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + PyDoc_STRVAR(_operator_contains__doc__, "contains($module, a, b, /)\n" "--\n" @@ -1489,4 +1559,4 @@ _operator__compare_digest(PyObject *module, PyObject *const *args, Py_ssize_t na exit: return return_value; } -/*[clinic end generated code: output=ddbba2cd943571eb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c240b42c5e1a2d8f input=a9049054013a1b77]*/