I am trying to understand how the primes iterator works, the code was taken from a lecture.
I searched for count() but found only methods for lists, objects, I just do not understand how the line self._candidates = count(1) works and what it means.
where is the object we are trying to count 1 in? and the further use of it self._candidates.next() is also very confusing.
I mainly code in java albeit know basic python.
here is the code:
class Primes(object):
def __init__(self):
self._candidates = count(1)
def __iter__(self): return self
def next(self):
item = self._candidates.next()
if item > 1:
self._candidates = FilterMultiplies(self._candidates, item)
return item
class FilterMultiplies(object):def __init__(self, seq, n):
self._seq = iter(seq)
self._n = n
def __iter__(self): return self
def next(self):
item = self._seq.next()
while item % self._n == 0:
item = self._seq.next()
return item
Probably this is itertools.count, and a line
from itertools import count
is missing from the listing.
Generators in Python are comparable to Iterators in Java. The call count(1) returns a generator that counts upwards from 1:
>>> from itertools import count
>>> counter = count(1)
>>> counter.next()
1
>>> counter.next()
2
>>> counter.next()
3
Note that counter.next() is Python 2 only. For compatibility with both Python 2 and 3, use next(counter) instead.
Related
I wrote a generator function that I instantiate then call over and over again, each time this increments the number.
def pagecnt():
n = 1
while True:
yield n
n += 1
pg = pagecnt()
print(next(pg))
print(next(pg))
print(next(pg))
This prints 1, 2 and then 3. Is there some way to combine the generator and instantiation into a new function so that I can just call
print(newfunc())
print(newfunc())
print(newfunc())
to get 1, 2 and 3?
EDIT: I don't just want to call it only 3 times. It's used to generate page number. So I don't know in advance how many times I am going to call it. And between each call, there's lots of code to calculate stuff and generate graphs.
Just create a function which instantiates your generator and calls next n times
def combine_func(n):
pg = pagecnt()
for _ in range(n):
print(next(pg))
Or we can define a wrapper function which takes in the generator instance, and returns a function which we can call to get the next element
def combine_func(pg):
def next_elem():
return next(pg)
return next_elem
pg = pagecnt()
cf = combine_func(pg)
print(cf())
print(cf())
print(cf())
You could make a simple Counter class with __call__() defined:
class Counter():
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
c = Counter()
print(c())
print(c())
print(c())
prints:
1
2
3
You could also capture the iterator in a closure and return a lambda function:
from itertools import count
def Counter():
it = count(1)
return lambda: next(it)
c = Counter()
print(c())
print(c())
print(c())
prints the same as above. In both cases it would be easy to pass in a start value if you wanted to begin somewhere other than 0.
Edit:
This is the same as using count but with your custom generator:
def Counter():
def pagecnt():
n = 1
while True:
yield n
n += 1
it = pagecnt()
return lambda: next(it)
c = Counter()
print(c())
print(c())
print(c())
I have the following code which I wrote for a course which runs fine:
def reverse_iter(iterable):
"""Return a generator that yields items from iterable in reverse order"""
last = len(iterable) - 1
while last >= 0:
yield iterable[last]
last -= 1
The next part of the assignment is to turn this function into a class. I know this not practical but it is what is being asked. I have very limited knowledge about classes but I came up with the following code:
class ReverseIter:
"""Class whose instances iterate the initial iterable in reverse order"""
def __init__(self, iterable):
self.iterable = iterable
def iterreverse(self):
last = len(self.iterable) - 1
while last >= 0:
yield self.iterable[last]
last -= 1
nums = [1, 2, 3, 4]
it = ReverseIter(nums)
print(iter(it) is it)
print(next(it) == 4)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
The output always reads TypeError: 'ReverseIter' object is not iterable. I hav eno idea why this is coming up. Any explanations or suggestions would be helpful. The output of the print statements above by the way should be True, True, 3, 2, 1, Stop Iteration respectively.
An object is an iterator if it has a __next__ method, which is called to get the next item (and should raise StopIteration when it runs out), and __iter__ which returns an iterator (usually just self)
class ReverseIter:
def __init__(self, sequence):
self.sequence = sequence
self.index = len(sequence)
def __iter__(self):
return self
def __next__(self):
if self.index > 0:
self.index -= 1
return self.sequence[self.index]
else:
raise StopIteration
you must implement the __next__ method, as code below.
class ReverseIter:
"""Class whose instances iterate the initial iterable in reverse order"""
def __init__(self, iterable):
self.iterable = iterable
def __iter__(self):
last = len(self.iterable) - 1
while last >= 0:
yield self.iterable[last]
last -= 1
I have two iterators in python and both should follow the same "random" distribution (both should run in parallel). For instance:
class Iter1(object):
def __iter__(self):
for i in random_generator():
yield i
class Iter2(object):
def __iter__(self):
for i in random_generator():
yield i
for el1, el2 in zip(Iter1(), Iter2()):
print '{} {}'.format(el1, el2)
output should be somethig like:
0.53534 0.53534
0.12312 0.12312
0.19238 0.19238
How can I define random_generator() in a way that it creates the same random distributions in parallel for both iterators.
Note:
They should run in parallel
I can't generate the sequence in advance (it is a streaming, so I don't know the size of the sequence)
Thanks.
Specify the same seed to each call of random_generator:
import random
def random_generator(l, seed=None):
r = random.Random(seed)
for i in range(l):
yield r.random()
class Iter1(object):
def __init__(self, seed):
self.seed = seed
def __iter__(self):
for i in random_generator(10, self.seed):
yield i
class Iter2(object):
def __init__(self, seed):
self.seed = seed
def __iter__(self):
for i in random_generator(10, self.seed):
yield i
# The seed can be any hashable object, but don't use None; that
# tells random.seed() to use the current time. But make sure that
# Python itself isn't using hash randomization.
common_seed = object()
for el1, el2 in zip(Iter1(common_seed), Iter2(common_seed)):
print '{} {}'.format(el1, el2)
There is no way to control the random generation number in this way. If you want to do that you should create your own random function. But as another pythonic and simpler way you can just create one object and use itertools.tee in order to copy your iterator object to having the same result for your random sequences:
In [28]: class Iter1(object):
def __init__(self, number):
self.number = number
def __iter__(self):
for _ in range(self.number):
yield random.random()
....:
In [29]:
In [29]: num = Iter1(5)
In [30]: from itertools import tee
In [31]: num, num2 = tee(num)
In [32]: list(zip(num, num2))
Out[32]:
[(0.485400998727448, 0.485400998727448),
(0.8801649381536764, 0.8801649381536764),
(0.9684025615967844, 0.9684025615967844),
(0.9980073706742334, 0.9980073706742334),
(0.1963579685642387, 0.1963579685642387)]
From two options below what is more appealing(readable code, more pythonic, efficiency , etc..) for running through iterable and if I want add more logic in the future(for example add 1 to every returned value)?
One of options returns generator, the other returns next item and None when done all iterations.
Is there a preferable method? If yes, why? Is there limitations some method has that another don't.
class Option1():
def __init__(self):
self.current = 0
def get_next_batch(self, my_list):
if self.current == len(my_list):
self.current = 0
return None
self.current += 1
return [my_list[self.current-1]]
class Option2():
def __init__(self):
self.current = 0
def get_next_batch(self, my_list):
while self.current < len(my_list):
yield [my_list[self.current]]
self.current += 1
self.current = 0
raise StopIteration()
Usage:
o1 = Option1()
o2 = Option2()
arr = [1,2,3,4,5]
a = o1.get_next_batch(arr)
while a is not None:
print a
a = o1.get_next_batch(arr)
for item in o2.get_next_batch(arr):
print item
Output in both cases:
[1]
[2]
[3]
[4]
[5]
You almost certainly want to go with the second. Less lines, less opportunity for things to go wrong.
However, seeing as you're not using current outside the iterator I would just optimise the whole thing down to:
def get_next_batch(my_list):
for item in my_list:
yield [item]
arr = [1,2,3,4,5]
for item in get_next_batch(arr):
print item
Side points. Always make your classes inherit from object in python 2.7, and don't raise StopIteration to halt a generator in python -- it's deprecated behaviour that can lead to bugs. Use return instead. eg.
def get_next_batch(my_list):
for item in my_list:
if item > 3:
return
yield [item]
batched = list(get_next_batch([1,2,3,4,5]))
expected = [[1], [2], [3]]
print batched == expected
You can tidy up Option1 to make it easy to use in for loops. To do this you can make it with the iterator protocol. That is an __iter__ method that returns self and a next method to get the next item. eg.
class UsingNext(object):
def __init__(self, mylist):
self.current = 0
self.mylist = mylist
def __iter__(self):
return self
def next(self): # __next__ in python 3
if self.current >= len(self.mylist):
raise StopIteration
if self.current == 2:
self.current += 1
return "special"
item = self.mylist[self.current]
self.current += 1
return [item]
class UsingYield(object):
def __init__(self, mylist):
self.mylist = mylist
def __iter__(self):
for current, item in enumerate(self.mylist):
if current == 2:
yield "special"
continue
yield [item]
arr = range(1, 6)
# both print
# [1]
# [2]
# special
# [4]
# [5]
for item in UsingNext(arr):
print item
for item in UsingYield(arr):
print item
In my mind the generator version is cleaner, and easier to understand.
I am trying to generate the following sequence:
011212201220200112 ... constructed as follows: first is 0,
then repeated the following action:
already written part is attributed to the right with replacement
0 to 1, 1 to 2, 2 to 0.
E.g.
0 -> 01 -> 0112 -> 01121220 -> ...
I am trying to find the 3 billion-th element of this sequence.
I realized that the sequence grows exponentially and hence derived that:
log(base2) (3 billion) ~ 32
So I just need to generate this sequence 32 times.
Here is what I tried in python:
import os
import sys
s=['0']
num_dict = {'0':'1' , '1':'2' , '2':'0'}
def mapper(b):
return num_dict[b]
def gen(s):
while True:
yield s
s.extend( map(mapper,s) )
a = gen(s)
for i in xrange(32):
a.next()
print a.next()[3000000000 - 1]
The problem is my RAM gets filled up before hitting the 3 billion mark.
Is there a better way to do this problem ?
EDIT: This program could crash your machine.Please try for xrange(25) for testing purposes
There are enough hints in the comments that you should be able to find the one-line solution. I think that it's more interesting to try to derive it with a more general tool, namely, implicit data structures. Here's a class for singleton lists.
class Singleton:
def __init__(self, x):
self.x = x
def __getitem__(self, i):
if not isinstance(i, int): raise TypeError(i)
elif not (0 <= i < len(self)): raise IndexError(i)
else: return self.x
def __len__(self): return 1
We can use this class like so.
>>> lst = Singleton(42)
>>> lst[0]
42
>>> len(lst)
1
Now we define a concatenation class and a mapper class, where the latter takes a function and implicitly applies it to each list element.
class Concatenation:
def __init__(self, lst1, lst2):
self.lst1 = lst1
self.lst2 = lst2
self.cachedlen = len(lst1) + len(lst2)
def __getitem__(self, i):
if not isinstance(i, int): raise TypeError(i)
elif not (0 <= i < len(self)): raise IndexError(i)
elif i < len(self.lst1): return self.lst1[i]
else: return self.lst2[i - len(self.lst1)]
def __len__(self): return self.cachedlen
class Mapper:
def __init__(self, f, lst):
self.f = f
self.lst = lst
def __getitem__(self, i): return self.f(self.lst[i])
def __len__(self): return len(self.lst)
Now let's rewrite your code to use these classes.
a = Singleton(0)
for i in range(32):
a = Concatenation(a, Mapper({0: 1, 1: 2, 2: 0}.get, a))
print(a[3000000000 - 1])
As an exercise: why do we need cachedlen?