forked from PythonCharmers/python-future
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnewrange.py
More file actions
159 lines (127 loc) · 4.92 KB
/
newrange.py
File metadata and controls
159 lines (127 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""
Nearly identical to xrange.py, by Dan Crosta, from
https://github.com/dcrosta/xrange.git
This is included here in the ``future`` package rather than pointed to as
a dependency because there is no package for ``xrange`` on PyPI. It is
also tweaked to appear like a regular Python 3 ``range`` object rather
than a Python 2 xrange.
From Dan Crosta's README:
"A pure-Python implementation of Python 2.7's xrange built-in, with
some features backported from the Python 3.x range built-in (which
replaced xrange) in that version."
Read more at
https://late.am/post/2012/06/18/what-the-heck-is-an-xrange
"""
from __future__ import absolute_import
from collections import Sequence, Iterator
from itertools import islice
from future.backports.misc import count # with step parameter on Py2.6
# For backward compatibility with python-future versions < 0.14.4:
_count = count
class newrange(Sequence):
"""
Pure-Python backport of Python 3's range object. See `the CPython
documentation for details:
<http://docs.python.org/py3k/library/functions.html#range>`_
"""
def __init__(self, *args):
if len(args) == 1:
start, stop, step = 0, args[0], 1
elif len(args) == 2:
start, stop, step = args[0], args[1], 1
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('range() requires 1-3 int arguments')
try:
start, stop, step = int(start), int(stop), int(step)
except ValueError:
raise TypeError('an integer is required')
if step == 0:
raise ValueError('range() arg 3 must not be zero')
elif step < 0:
stop = min(stop, start)
else:
stop = max(stop, start)
self._start = start
self._stop = stop
self._step = step
self._len = (stop - start) // step + bool((stop - start) % step)
@property
def start(self):
return self._start
@property
def stop(self):
return self._stop
@property
def step(self):
return self._step
def __repr__(self):
if self._step == 1:
return 'range(%d, %d)' % (self._start, self._stop)
return 'range(%d, %d, %d)' % (self._start, self._stop, self._step)
def __eq__(self, other):
return (isinstance(other, newrange) and
(self._len == 0 == other._len or
(self._start, self._step, self._len) ==
(other._start, other._step, self._len)))
def __len__(self):
return self._len
def index(self, value):
"""Return the 0-based position of integer `value` in
the sequence this range represents."""
diff = value - self._start
quotient, remainder = divmod(diff, self._step)
if remainder == 0 and 0 <= quotient < self._len:
return abs(quotient)
raise ValueError('%r is not in range' % value)
def count(self, value):
"""Return the number of ocurrences of integer `value`
in the sequence this range represents."""
# a value can occur exactly zero or one times
return int(value in self)
def __contains__(self, value):
"""Return ``True`` if the integer `value` occurs in
the sequence this range represents."""
try:
self.index(value)
return True
except ValueError:
return False
def __reversed__(self):
return iter(self[::-1])
def __getitem__(self, index):
"""Return the element at position ``index`` in the sequence
this range represents, or raise :class:`IndexError` if the
position is out of range."""
if isinstance(index, slice):
return self.__getitem_slice(index)
if index < 0:
# negative indexes access from the end
index = self._len + index
if index < 0 or index >= self._len:
raise IndexError('range object index out of range')
return self._start + index * self._step
def __getitem_slice(self, slce):
"""Return a range which represents the requested slce
of the sequence represented by this range.
"""
scaled_indices = (self._step * n for n in slce.indices(self._len))
start_offset, stop_offset, new_step = scaled_indices
return newrange(self._start + start_offset,
self._start + stop_offset,
new_step)
def __iter__(self):
"""Return an iterator which enumerates the elements of the
sequence this range represents."""
return range_iterator(self)
class range_iterator(Iterator):
"""An iterator for a :class:`range`.
"""
def __init__(self, range_):
self._stepper = islice(count(range_.start, range_.step), len(range_))
def __iter__(self):
return self
def next(self):
return next(self._stepper)
__all__ = ['newrange']