From f5bd3b442dd378563036f51595a7d6b2a239f4d5 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 29 Dec 2005 16:50:42 +0000 Subject: adding in-place operators to the operator module. --- Doc/lib/liboperator.tex | 102 ++++++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_operator.py | 47 +++++++++++++++++++++ Modules/operator.c | 42 ++++++++++++++++++- 3 files changed, 190 insertions(+), 1 deletion(-) diff --git a/Doc/lib/liboperator.tex b/Doc/lib/liboperator.tex index 2507307..11e004a 100644 --- a/Doc/lib/liboperator.tex +++ b/Doc/lib/liboperator.tex @@ -237,6 +237,108 @@ sequence \var{v}. \end{funcdesc} +Many operations have an ``in-place'' version. The following functions +provide a more primitive access to in-place operators than the usual +syntax does; for example, the statement \code{x += y} is equivalent to +\code{x = operator.iadd(x, y)}. Another way to put it is to say that +\code{z = operator.iadd(x, y)} is equivalent to the compound statement +\code{z = x; z += y}. + +\begin{funcdesc}{iadd}{a, b} +\funcline{__iadd__}{a, b} +\code{a = iadd(a, b)} is equivalent to \code{a += b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{iand}{a, b} +\funcline{__iand__}{a, b} +\code{a = iand(a, b)} is equivalent to \code{a \&= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{iconcat}{a, b} +\funcline{__iconcat__}{a, b} +\code{a = iconcat(a, b)} is equivalent to \code{a += b} for \var{a} +and \var{b} sequences. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{idiv}{a, b} +\funcline{__idiv__}{a, b} +\code{a = idiv(a, b)} is equivalent to \code{a /= b} when +\code{__future__.division} is not in effect. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{ifloordiv}{a, b} +\funcline{__ifloordiv__}{a, b} +\code{a = ifloordiv(a, b)} is equivalent to \code{a //= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{ilshift}{a, b} +\funcline{__ilshift__}{a, b} +\code{a = ilshift(a, b)} is equivalent to \code{a <}\code{<= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{imod}{a, b} +\funcline{__imod__}{a, b} +\code{a = imod(a, b)} is equivalent to \code{a \%= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{imul}{a, b} +\funcline{__imul__}{a, b} +\code{a = imul(a, b)} is equivalent to \code{a *= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{ior}{a, b} +\funcline{__ior__}{a, b} +\code{a = ior(a, b)} is equivalent to \code{a |= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{ipow}{a, b} +\funcline{__ipow__}{a, b} +\code{a = ipow(a, b)} is equivalent to \code{a **= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{irepeat}{a, b} +\funcline{__irepeat__}{a, b} +\code{a = irepeat(a, b)} is equivalent to \code{a *= b} where +\var{a} is a sequence and \var{b} is an integer. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{irshift}{a, b} +\funcline{__irshift__}{a, b} +\code{a = irshift(a, b)} is equivalent to \code{a >}\code{>= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{isub}{a, b} +\funcline{__isub__}{a, b} +\code{a = isub(a, b)} is equivalent to \code{a -= b}. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{itruediv}{a, b} +\funcline{__itruediv__}{a, b} +\code{a = itruediv(a, b)} is equivalent to \code{a /= b} when +\code{__future__.division} is in effect. +\versionadded{2.5} +\end{funcdesc} + +\begin{funcdesc}{ixor}{a, b} +\funcline{__ixor__}{a, b} +\code{a = ixor(a, b)} is equivalent to \code{a \textasciicircum= b}. +\versionadded{2.5} +\end{funcdesc} + + The \module{operator} module also defines a few predicates to test the type of objects. \note{Be careful not to misinterpret the results of these functions; only \function{isCallable()} has any diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index 6cc7945..c1fe88c 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -412,6 +412,53 @@ class OperatorTestCase(unittest.TestCase): self.assertEqual(operator.itemgetter(2,10,5)(data), ('2', '10', '5')) self.assertRaises(TypeError, operator.itemgetter(2, 'x', 5), data) + def test_inplace(self): + class C(object): + def __iadd__ (self, other): return "iadd" + def __iand__ (self, other): return "iand" + def __idiv__ (self, other): return "idiv" + def __ifloordiv__(self, other): return "ifloordiv" + def __ilshift__ (self, other): return "ilshift" + def __imod__ (self, other): return "imod" + def __imul__ (self, other): return "imul" + def __ior__ (self, other): return "ior" + def __ipow__ (self, other): return "ipow" + def __irshift__ (self, other): return "irshift" + def __isub__ (self, other): return "isub" + def __itruediv__ (self, other): return "itruediv" + def __ixor__ (self, other): return "ixor" + def __getitem__(self, other): return 5 # so that C is a sequence + c = C() + self.assertEqual(operator.iadd (c, 5), "iadd") + self.assertEqual(operator.iand (c, 5), "iand") + self.assertEqual(operator.idiv (c, 5), "idiv") + self.assertEqual(operator.ifloordiv(c, 5), "ifloordiv") + self.assertEqual(operator.ilshift (c, 5), "ilshift") + self.assertEqual(operator.imod (c, 5), "imod") + self.assertEqual(operator.imul (c, 5), "imul") + self.assertEqual(operator.ior (c, 5), "ior") + self.assertEqual(operator.ipow (c, 5), "ipow") + self.assertEqual(operator.irshift (c, 5), "irshift") + self.assertEqual(operator.isub (c, 5), "isub") + self.assertEqual(operator.itruediv (c, 5), "itruediv") + self.assertEqual(operator.ixor (c, 5), "ixor") + self.assertEqual(operator.iconcat (c, c), "iadd") + self.assertEqual(operator.irepeat (c, 5), "imul") + self.assertEqual(operator.__iadd__ (c, 5), "iadd") + self.assertEqual(operator.__iand__ (c, 5), "iand") + self.assertEqual(operator.__idiv__ (c, 5), "idiv") + self.assertEqual(operator.__ifloordiv__(c, 5), "ifloordiv") + self.assertEqual(operator.__ilshift__ (c, 5), "ilshift") + self.assertEqual(operator.__imod__ (c, 5), "imod") + self.assertEqual(operator.__imul__ (c, 5), "imul") + self.assertEqual(operator.__ior__ (c, 5), "ior") + self.assertEqual(operator.__ipow__ (c, 5), "ipow") + self.assertEqual(operator.__irshift__ (c, 5), "irshift") + self.assertEqual(operator.__isub__ (c, 5), "isub") + self.assertEqual(operator.__itruediv__ (c, 5), "itruediv") + self.assertEqual(operator.__ixor__ (c, 5), "ixor") + self.assertEqual(operator.__iconcat__ (c, c), "iadd") + self.assertEqual(operator.__irepeat__ (c, 5), "imul") def test_main(verbose=None): import sys diff --git a/Modules/operator.c b/Modules/operator.c index 3223ce3..ddd0252 100644 --- a/Modules/operator.c +++ b/Modules/operator.c @@ -80,9 +80,23 @@ spami(op_not_ , PyObject_Not) spam2(op_and_ , PyNumber_And) spam2(op_xor , PyNumber_Xor) spam2(op_or_ , PyNumber_Or) +spam2(op_iadd , PyNumber_InPlaceAdd) +spam2(op_isub , PyNumber_InPlaceSubtract) +spam2(op_imul , PyNumber_InPlaceMultiply) +spam2(op_idiv , PyNumber_InPlaceDivide) +spam2(op_ifloordiv , PyNumber_InPlaceFloorDivide) +spam2(op_itruediv , PyNumber_InPlaceTrueDivide) +spam2(op_imod , PyNumber_InPlaceRemainder) +spam2(op_ilshift , PyNumber_InPlaceLshift) +spam2(op_irshift , PyNumber_InPlaceRshift) +spam2(op_iand , PyNumber_InPlaceAnd) +spam2(op_ixor , PyNumber_InPlaceXor) +spam2(op_ior , PyNumber_InPlaceOr) spami(isSequenceType , PySequence_Check) spam2(op_concat , PySequence_Concat) spamoi(op_repeat , PySequence_Repeat) +spam2(op_iconcat , PySequence_InPlaceConcat) +spamoi(op_irepeat , PySequence_InPlaceRepeat) spami2b(op_contains , PySequence_Contains) spami2b(sequenceIncludes, PySequence_Contains) spami2(indexOf , PySequence_Index) @@ -108,6 +122,15 @@ op_pow(PyObject *s, PyObject *a) } static PyObject* +op_ipow(PyObject *s, PyObject *a) +{ + PyObject *a1, *a2; + if (PyArg_UnpackTuple(a,"ipow", 2, 2, &a1, &a2)) + return PyNumber_InPlacePower(a1, a2, Py_None); + return NULL; +} + +static PyObject* is_(PyObject *s, PyObject *a) { PyObject *a1, *a2, *result = NULL; @@ -224,17 +247,34 @@ spam2o(not_,__not__, "not_(a) -- Same as not a.") spam2(and_,__and__, "and_(a, b) -- Same as a & b.") spam2(xor,__xor__, "xor(a, b) -- Same as a ^ b.") spam2(or_,__or__, "or_(a, b) -- Same as a | b.") +spam2(iadd,__iadd__, "iadd(a, b) -- Same as a += b.") +spam2(isub,__isub__, "isub(a, b) -- Same as a -= b.") +spam2(imul,__imul__, "imul(a, b) -- Same as a *= b.") +spam2(idiv,__idiv__, "idiv(a, b) -- Same as a /= b when __future__.division is not in effect.") +spam2(ifloordiv,__ifloordiv__, "ifloordiv(a, b) -- Same as a //= b.") +spam2(itruediv,__itruediv__, "itruediv(a, b) -- Same as a /= b when __future__.division is in effect.") +spam2(imod,__imod__, "imod(a, b) -- Same as a %= b.") +spam2(ilshift,__ilshift__, "ilshift(a, b) -- Same as a <<= b.") +spam2(irshift,__irshift__, "irshift(a, b) -- Same as a >>= b.") +spam2(iand,__iand__, "iand(a, b) -- Same as a &= b.") +spam2(ixor,__ixor__, "ixor(a, b) -- Same as a ^= b.") +spam2(ior,__ior__, "ior(a, b) -- Same as a |= b.") spam2(concat,__concat__, "concat(a, b) -- Same as a + b, for a and b sequences.") spam2(repeat,__repeat__, "repeat(a, b) -- Return a * b, where a is a sequence, and b is an integer.") +spam2(iconcat,__iconcat__, + "iconcat(a, b) -- Same as a += b, for a and b sequences.") +spam2(irepeat,__irepeat__, + "irepeat(a, b) -- Same as a *= b, where a is a sequence, and b is an integer.") spam2(getitem,__getitem__, "getitem(a, b) -- Same as a[b].") spam2(setitem,__setitem__, "setitem(a, b, c) -- Same as a[b] = c.") spam2(delitem,__delitem__, "delitem(a, b) -- Same as del a[b].") -spam2(pow,__pow__, "pow(a, b) -- Same as a**b.") +spam2(pow,__pow__, "pow(a, b) -- Same as a ** b.") +spam2(ipow,__ipow__, "ipow(a, b) -- Same as a **= b.") spam2(getslice,__getslice__, "getslice(a, b, c) -- Same as a[b:c].") spam2(setslice,__setslice__, -- cgit v0.12