From 47d159f5220918f9c1216ba93583f3aa740a8944 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 16 Aug 2015 19:43:34 -0700 Subject: [PATCH] Issue #24379: Add operator.subscript() as a convenience for building slices. --- Doc/library/operator.rst | 15 +++++++++++++++ Doc/whatsnew/3.6.rst | 7 ++++++- Lib/operator.py | 28 +++++++++++++++++++++++++++- Lib/test/test_operator.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst index c01e63b77a9..06953917e78 100644 --- a/Doc/library/operator.rst +++ b/Doc/library/operator.rst @@ -333,6 +333,21 @@ expect a function argument. [('orange', 1), ('banana', 2), ('apple', 3), ('pear', 5)] +.. data:: subscript + + A helper to turn subscript notation into indexing objects. This can be + used to create item access patterns ahead of time to pass them into + various subscriptable objects. + + For example: + + * ``subscript[5] == 5`` + * ``subscript[3:7:2] == slice(3, 7, 2)`` + * ``subscript[5, 8] == (5, 8)`` + + .. versionadded:: 3.6 + + .. function:: methodcaller(name[, args...]) Return a callable object that calls the method *name* on its operand. If diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index e2aa1e2f02c..48ff38a241f 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -95,7 +95,12 @@ New Modules Improved Modules ================ -* None yet. +operator +-------- + +* New object :data:`operator.subscript` makes it easier to create complex + indexers. For example: ``subscript[0:10:2] == slice(0, 10, 2)`` + (Contributed by Joe Jevnik in :issue:`24379`.) Optimizations diff --git a/Lib/operator.py b/Lib/operator.py index 0e2e53efc69..bc2a9478b8b 100644 --- a/Lib/operator.py +++ b/Lib/operator.py @@ -17,7 +17,7 @@ __all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf', 'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint', 'lshift', 'lt', 'matmul', 'methodcaller', 'mod', 'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', - 'setitem', 'sub', 'truediv', 'truth', 'xor'] + 'setitem', 'sub', 'subscript', 'truediv', 'truth', 'xor'] from builtins import abs as _abs @@ -408,6 +408,32 @@ def ixor(a, b): return a +@object.__new__ # create a singleton instance +class subscript: + """ + A helper to turn subscript notation into indexing objects. This can be + used to create item access patterns ahead of time to pass them into + various subscriptable objects. + + For example: + subscript[5] == 5 + subscript[3:7:2] == slice(3, 7, 2) + subscript[5, 8] == (5, 8) + """ + __slots__ = () + + def __new__(cls): + raise TypeError("cannot create '{}' instances".format(cls.__name__)) + + @staticmethod + def __getitem__(key): + return key + + @staticmethod + def __reduce__(): + return 'subscript' + + try: from _operator import * except ImportError: diff --git a/Lib/test/test_operator.py b/Lib/test/test_operator.py index 54fd1f4e528..27501c24669 100644 --- a/Lib/test/test_operator.py +++ b/Lib/test/test_operator.py @@ -596,5 +596,38 @@ class CCOperatorPickleTestCase(OperatorPickleTestCase, unittest.TestCase): module2 = c_operator +class SubscriptTestCase: + def test_subscript(self): + subscript = self.module.subscript + self.assertIsNone(subscript[None]) + self.assertEqual(subscript[0], 0) + self.assertEqual(subscript[0:1:2], slice(0, 1, 2)) + self.assertEqual( + subscript[0, ..., :2, ...], + (0, Ellipsis, slice(2), Ellipsis), + ) + + def test_pickle(self): + from operator import subscript + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(proto=proto): + self.assertIs( + pickle.loads(pickle.dumps(subscript, proto)), + subscript, + ) + + def test_singleton(self): + with self.assertRaises(TypeError): + type(self.module.subscript)() + + def test_immutable(self): + with self.assertRaises(AttributeError): + self.module.subscript.attr = None + + +class PySubscriptTestCase(SubscriptTestCase, PyOperatorTestCase): + pass + + if __name__ == "__main__": unittest.main()