Arithmetic Progression in Python without storing all the values
I'm trying to represent an array of evenly spaced floats, an arithmetic progression, starting at a0 and with elements a0, a0 + a1, a0 + 2a1, a0 + 3a1, ... This is what numpy's arange() method does, but it seems to allocate memory for the whole array object and I'd like to do it using an iterator class which just stores a0, a1 and n (the total number of elements, which might be large). Does anything that does this already exist in the standard Python packages? I couldn't find it so, ploughed ahead with:
class mylist():
def __init__(self, n, a0, a1):
self._n = n
self._a0 = a0
self._a1 = a1
def __getitem__(self, 开发者_开发知识库i):
if i < 0 or i >= self._n:
raise IndexError
return self._a0 + i * self._a1
def __iter__(self):
self._i = 0
return self
def next(self):
if self._i >= self._n:
raise StopIteration
value = self.__getitem__(self._i)
self._i += 1
return value
Is this a sensible approach or am I revinventing the wheel?
Well, one thing that you are doing wrong is that it should be for i, x in enumerate(a): print i, x
.
Also, I'd probably use a generator method instead of the hassle with the __iter__
and next()
methods, especially because your solution wouldn't allow you to iterate over the same mylist
twice at the same time with two different iterators (as self._i
is local to the class).
This is probably a better solution which gives you random access as well as an efficient iterator. The support for the in
and len
operators are thrown in as a bonus :)
class mylist(object):
def __init__(self, n, a0, a1, eps=1e-8):
self._n = n
self._a0 = a0
self._a1 = a1
self._eps = eps
def __contains__(self, x):
y = float(x - self._a0) / self._a1
return 0 <= int(y) < self._n and abs(y - int(y)) < self._eps
def __getitem__(self, i):
if 0 <= i < self._n:
return self._a0 + i * self._a1
raise IndexError
def __iter__(self):
current = self._a0
for i in xrange(self._n):
yield current
current += self._a1
def __len__(self):
return self._n
Other answers answer the immediate problem. Note that if all you want is an iterator and you don't need random access, there's no need to write a whole iterator class.
def mylist(n, a0, a1):
for i in xrange(n):
yield a0 + i*a1
Only for reasons that are probably obvious to someone out there, iterating over mylist: for i,x in enumerate(a): print i,a doesn't return the values I expect but just a whole lot of references to the mylist instance. What am I doing wrong?
The culprit is print i,a
. You are printing a
, which is an array. You ought to print x
instead. Chane this line to:
print i,x
Also a couple of things:
- Change you class name to TitleCase. For e.g.
class MyList
orclass Mylist
. - It is a good idea to inherit from
object
if you are using Python 2.x. Soclass MyList(object): ...
You're enumerating over a
, so printing it will print a lot of references to it. Print x
instead.
Yes, there's a built-in generator for this (Python 2.7 and up):
import itertools
mygen = itertools.count(a0,a1)
If you don't have Python 2.7 yet (Python 2.4 and up):
import itertools
mygen = (a0 + a1*i for i in itertools.count())
精彩评论