Python: list comprehension which references an internally generated list - python

First off, there isn't a question if you break this problem into three steps: 1) create some_list 2) create random_list and 3) run a straight forward list comprehension over the two lists (i.e. [(x, y, f(y, m)) for x in l for y in m]. Because of the constraints of some APIs I am working with, I want be able to write the code with two lines: 1) create some_list 2) run a list comprehension which creates the second second list and allows for some operation/function/method against the internally generated list itself.
This is what I would like to be able to do:
import random
[(x, y, not random_list.index(y)) for x in some_list
for y in random.sample([1,2,3,4,5], (random.choice([1,2,3]))) as random_list]
I know the as random_list doesn't work here. I know if I create a list based on random.sample([1,2,3,4,5], random.choice([1,2,3])) before I code the list comprehension, there isn't any problem here.
Writing out the question leads to some answers. Here is the specific answer to the specific problem given above:
[(x, y, not i) for x in some_list
for i, y in enumerate(random.sample([1,2,3,4,5], random.choice([1,2,3])))]
My basic question is: if a list is created inside a list comprehension, can that list itself be referenced within the list comprehension? Or do I need to create some kind of wrapper?
def wrapper():
f = lambda i, l: 42
l = random_list()
return [(i, f(i, l)) for i in l]
[(x, i, v) for x in some_list for i, v in wrapper()]
I think I have largely answered my question, but it seemed like a good one worth writing up. If there are other thoughts, comments, that would be very useful.
#DSM valid question. Here is the particular (django) code:
PlayerPosition.objects.bulk_create([
PlayerPosition(
player=player,
position_id=position_id,
primary=not index
) for player in Player.objects.all()
for index, position_id in enumerate(random_position_list())
])

Essentially you want to create a list, bind it to a name, and then use the name inside a list comprehension. What prevents binding a name in an expression is that assignments are statements. However, you can (ab)use lambdas to get what you want:
(lambda random_list: [
(x, y, not random_list.index(y))
for x in some_list
for y in random_list])(
random_list=random.sample([1, 2, 3, 4, 5], random.choice([1, 2, 3])))
That works in that it's one line of code, but I'm not sure if it works within your other constraints, which are at this point enigmatic and unclear.
EDIT: If you can make a helper function then you should almost certainly take that approach. I would do it differently than yours, though. I'd make a generic one like this:
def iter_with_list(l):
for element in l:
yield (l, element)
Using it like so:
[(x, y, not random_list.index(y))
for x in some_list
for random_list, y in iter_with_list(random.sample([1,2,3,4,5], random.choice([1, 2, 3])))]
That way you can still keep all the logic in the comprehension without spreading it all over the place. This is just another trick to bind a name as part of an expression - in this case by repeatedly offering it up to be bound for each iteration of the comprehension - but it's a lot more elegant than the previous one.

My basic question is: if a list is created inside a list comprehension, can that list itself be referenced within the list comprehension?
Short answer is, yes it can. You can have as many list comprehensions inside list comprehensions as you like, but things will get quite unreadable. Demonstration:
>>> [var for var in [val for val in range(1, 100)]]
[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]
>>>
If you want to combine two lists, as tuples:
You can do something like this:
>>> from random import randint
>>> zip([x for x in xrange(0, 10)], [randint(0, 100) for _ in xrange(10)])
[(0, 56), (1, 60), (2, 7), (3, 29), (4, 85), (5, 76), (6, 95), (7, 91), (8, 40), (9, 4)]
You can even apply operations on the values of list comprehensions (but it gets really unreadable then):
>>> weird_list = [val for val in [var for var in xrange(0, 100) if not var % 2] if not val % 3]
>>> print weird_list
[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
With regard to your comment:
>>> another_weird_list = [(var, 42) for var in xrange(5)]
>>> another_weird_list
[(0, 42), (1, 42), (2, 42), (3, 42), (4, 42)]
No need for a lambda.
In response to your second comment:
Initially:
arbitrary_list = [x for x in range(10)]
f = lambda x: [var + 1 for var in x]
no_list = [(var, f(arbitrary_list)) for var in arbitrary_list]
Then:
no_list = [(var, [var + 1 for var in [x for x in range(10)]]) for var in [x for x in range(10)]]
One liner. You can still make an arbitrary_list if you want.

Related

Alternate Python List Reverse Solution Needed

I had a job interview today. During it I was asked to write down an algorithm that will reverse a list. First I offered the answer using the reversed() method:
x = [1,2,3,4,5]
y = reversed(x)
for i in y:
print i
The senior developer conducting the interview asked me if I know another way, upon which I wrote down the other known method with slicing:
x = [1,2,3,4,5]
y = x[::-1]
Unfortunately he was unhappy with this solution as well and asked me to think of another one. After few minutes I said I could not come up with a better one. He said that this was not good enough for their standards.
I am perfectly fine with his opinion and have no problem practicing more on my code. My question is, what is a better solution that I am not aware of, if there is one. Could there be some other more 'programmer' way...Only other thing that comes to mind is recursion, however I thought of it only after the interview was already done.
Thanks.
Both your answers are good in terms of python so the interviewer must have been asking you to implement your own method:
Using recursion:
def recur_rev(l):
return recur_rev(l[1:]) + l[:1] if l else l
Or a list comp and range starting at the length of l -1 and going in reverse:
l = list(range(100))
print([l[ind] for ind in range(len(l)-1,-1,-1)])
Using itertools.count:
from itertools import count
cn = count(len(l) -1, -1)
print([l[next(cn)] for ele in l])
For efficiency use a generator expression:
rev = (l[next(cn)] for ele in l)
for ele in rev:
print(ele)
Or using map:
print(list(map(l.__getitem__,range(len(l)-1,-1,-1)))) # list needed for python3
[99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
without the list call on map we will get a map object we can iterate over in python3, you can use itertools.imap in python2 to achieve a similar result
I guess your interviewer wanted to hear something like this:
A straightforward way to revert a list is to find out its length, then iterate from len-1 downto 0 and append the i-th element to the result. This approach works fine with real lists, but how can we revert a generator, i.e. something whose length is not known and cannot be found? (Good pythonistas use generators and yields instead of lists and returns).
Here's where we need a data structure known as "stack". A stack is like, well, a stack of things, like plates, put on the top of each other.
Whatever you put last comes out first, whatever was put first comes out last. So, our strategy will be like this:
while there are items, put each item onto the stack
while the stack is not empty, pop items off the stack and return them
Stacks can be programmed in python using lists, where .append puts an item on the top of the stack, and .pop removes the last thing off the stack:
def reverse(it):
stack = []
for item in it:
stack.append(item)
while stack:
yield stack.pop()
Of course, Python already has a built-in stack, and it's the stack of call frames. We can use that instead of the simulated one above, which leads us to the following recursive solution:
def reverse(it):
head = next(it)
for item in reverse(it):
yield item
yield head
in python3, this is even more elegant:
def reverse(it):
head = next(it)
yield from reverse(it)
yield head
Again, this works with arbitrary iterables, whose length is unknown at the call time.
One simple algorithm that could be easily ported to other languages would be:
x = [1,2,3,4,5]
y = []
for i in len(x):
y.append(x[len(x) - 1 - i])
Or using an iterator. This could be easily ported to be used with a chained list where you don't know the length:
x = [1,2,3,4,5]
y = []
x_iterator = iter(x)
try:
while (1):
y.insert(0, x_iterator.next())
except:
print y
Or a more pythonic version with insert:
x = [1,2,3,4,5]
y = []
for elem in x:
y.insert(0, elem)
I'm assuming your interviewer didn't want you to use built-in Python methods, but an algorithm that's more language-agnostic. Something like:
lst = [1,2,3,4,5]
lst2 = []
while len(lst) > 0:
lst2.append(lst.pop())
or
lst2 = [lst.pop() for _ in lst[:]]
How about something like that:
x = [1, 2, 3, 4, 5]
for i in xrange(len(x) / 2):
x[i], x[-i - 1] = x[-i - 1], x[i]
print(x)
The idea is to swap array elements from opposite directions
I like this way:
def reverse(arr):
for i in range(len(arr) / 2):
arr[-i-1], arr[i] = arr[i], arr[-i-1]
Basically iterating through the first half of the array and swapping i and len(i)-i-1.
def reverse(text):
rev = []
for let in range(len(text),0,-1):
rev.append(text[let-1])
return ''.join(rev)

Is it possible to randomly *remove* a percentage/number of items from a list & then *append* them to another list?

I am new to python and programming, so apologies in advance. I know of remove(), append(), len(), and rand.rang (or whatever it is), and I believe I would need those tools, but it's not clear to me how to code it.
What I would like to do is, while looping or otherwise accessing List_A, randomly select an index within List_A, remove the selected_index from List_A, and then append() the selected_index to List_B.
I would like to randomly remove only up to a certain percentage (or real number if this is impossible) of items from List A.
Any ideas?? Is what I'm describing possible?
If you don't care about the order of the input list, I'd shuffle it, then remove n items from that list, adding those to the other list:
from random import shuffle
def remove_percentage(list_a, percentage):
shuffle(list_a)
count = int(len(list_a) * percentage)
if not count: return [] # edge case, no elements removed
list_a[-count:], list_b = [], list_a[-count:]
return list_b
where percentage is a float value between 0.0 and 1.0.
Demo:
>>> list_a = range(100)
>>> list_b = remove_percentage(list_a, 0.25)
>>> len(list_a), len(list_b)
(75, 25)
>>> list_b
[1, 94, 13, 81, 23, 84, 41, 92, 74, 82, 42, 28, 75, 33, 35, 62, 2, 58, 90, 52, 96, 68, 72, 73, 47]
If you can find a random index i of some element in listA, then you can easily move it from A to B using:
listB.append(listA.pop(i))
>>> lis = range(100)
>>> per = .30
>>> no_of_items = int( len(lis) * per) #number of items in 30 percent
>>> lis_b = []
>>> for _ in xrange(no_of_items):
ind = random.randint(0,len(lis)-1) #selects a random index value
lis_b.append(lis.pop(ind)) #pop the item at that index and append to lis_b
...
>>> lis_b
[73, 32, 82, 68, 90, 19, 3, 49, 21, 17, 30, 75, 1, 31, 80, 48, 38, 18, 99, 98, 4, 20, 33, 29, 66, 41, 64, 26, 77, 95]
1) Calculate how many elements you want to remove, call it k.
2) random.randrange(len(listA)) will return a random number between 0 and len(listA)-1 inclusive, e.g. a random index you can use in listA.
3) Grab the element at that index, remove it from listA, append it to listB.
4) Repeat until you have removed k elements.

Python Data structure index Start at 1 instead of 0?

I have a weird question: I have this list of 64 numbers that will never change:
(2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128)
I need a data structure in Python that will allow me to accsess these numbers using a 1-64 index as opposed to the standard 0-63. Is this possible? Would the best way to accomplish this be to build a dictionary?
Just insert a 0 at the beginning of the structure:
(0, 2, 4, 6, 8, ...)
You could override the item getter and make a specialized tuple:
class BaseOneTuple(tuple):
__slots__ = () # Space optimization, see: http://stackoverflow.com/questions/472000/python-slots
def __new__(cls, *items):
return tuple.__new__(cls, items) # Creates new instance of tuple
def __getitem__(self, n):
return tuple.__getitem__(self, n - 1)
b = BaseOneTuple(*range(2, 129, 2))
b[2] == 4
You could use a dictionary, or you could simply subtract one from your index before accessing it.
Also, I note that your 64 numbers are in a simple arithmetic progression. Why store them at all? You can use this:
def my_number(i):
return 2*i
If the list you showed was actually an example, and the real numbers are more complicated, then use a list with a dummy first element:
my_nums = [0, 2, 4, 6, 8, ....]
Then you can get 2 as my_nums[1].
You could use range(2, 129, 2) to generate the numbers in the range 1 - 128 in increments of 2 and convert this list into a tuple if it's not going to change.
t = tuple(range(2, 129, 2))
def numbers(n):
return t[n-1]
Given the global tuple t, function numbers could retrieve elements using a 1-based (instead of 0-based) index.

Python- Sieve of Eratosthenes- Compact Python

This is my code for finding primes using the Sieve of Eratosthenes.
list = [i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]
for i in list:
for a in list:
if a!=i and a%i == 0:
list.remove(a)
Trying to find a way to compress those nested for loops into some kind of generator or comprehension, but it doesn't seem that you can apply a function to a list using a comprehension. I tried using map and filter, but I can't seem to get it right.
Thinking about something like this:
print map(list.remove(a), filter(lambda a, i: (a%i ==0 and a!=i), [(a, i) for i in list for a in list])
Obviously doesn't work for a dozen reasons. If I just was using the filter portion of that code:
filter(lambda a, i: (a%i ==0 and a!=i), **[(a, i) for i in list for a in list]**
What's the proper way of putting two variables into the lambda? (a,i) makes it a tuple, but I want to submit 'a' and 'i' as independent variables to put into the lambda.
I ended up resolving the problem with this one-liner:
print sorted(set([i for i in range(2, int(raw_input("Compute primes up to what number? "))+1)]).difference(a for i in l for a in l if a!=i and a%i == 0))
The first thing to note is that what you have written is not the sieve of eratosthenes. Look how many loops a totally naive sieve of eratosthenes executes:
def sieve1(n):
loops = 0
numbers = set(range(2, n))
for i in range(2, int(n ** 0.5) + 1):
for j in range(i * 2, n, i):
numbers.discard(j)
loops += 1
return sorted(numbers), loops
Tested:
>>> sieve1(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
178)
178 loops for 100 numbers (not including the sort). This can be improved with a few minor changes:
def sieve2(n):
loops = 0
numbers = range(0, n)
for prime in numbers:
if prime < 2:
continue
elif prime > n ** 0.5:
break
for i in range(prime ** 2, n, prime):
numbers[i] = 0
loops += 1
return [x for x in numbers if x > 1], loops
Tested:
>>> sieve2(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
102)
102 loops for 100 numbers (not including the filter at the end). Now look at yours:
def sieve3(n):
loops = 0
numbers = range(2, n)
for i in numbers:
for j in numbers:
if j != i and j % i == 0:
numbers.remove(j)
loops += 1
return numbers, loops
Tested:
>>> sieve3(100)
([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
663)
It gets worse:
>>> [sieve1(x)[1] for x in [100, 1000, 10000]]
[178, 2978, 41723]
>>> [sieve2(x)[1] for x in [100, 1000, 10000]]
[102, 1409, 16979]
>>> [sieve3(x)[1] for x in [100, 1000, 10000]]
[663, 28986, 1523699]
At n = 10000, your implementation does almost 100x as much work!
My suggestion would be to create a sensible implementation before making it "compact." Code golf can be fun, but it's nowhere near as challenging or as edifying as writing efficient code, whatever the length.
That said, consider this one-liner (if you don't count the import, which you could get rid of by using lambda x, y: x - y in place of operator.sub). This implements the first algorithm with a small improvement:
>>> from operator import sub
>>> reduce(sub, (set(range(x ** 2, 100, x)) for x in range(2, int(100 ** 0.5) + 1)), set(range(2, 100)))
set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
It's not precisely a direct translation of your loops, but it's quite close and compact:
>>> l = range(2, 101)
>>> sorted(set(l).difference(a for i in l for a in l if a!=i and a%i == 0))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Although I'd suggest a > i rather than a != 0 as being shorter and faster ;)
You are not doing the Sieve of Eratosthenes; the danger of not properly implementing the algorithm is that it will be extremely slow. Try your algorithm on 10**6 for example.
Shortest implementation of the bounded Sieve of Eratosthenes I can come up with:
def primes(upTo):
isPrime = list(range(upTo))
for p in range(2,int(upTo**0.5)+1): #p: 2,3,4,...,sqrt(N)
print(p, isPrime[p])
if isPrime[p]:
for multiple in range(p**2,upTo,p): #mult: p^2, p^2+p, p^2+2p, ..., N
isPrime[multiple] = False
return [x for x in isPrime[2:] if x]
Demo:
>>> list(primes(29))
[2, 3, 5, 7, 11, 13, 17, 19, 23]
It's actually rather succinct, if you ignore linebreaks and the massive skip-even-numbers optimization:
isPrime=[True]*upTo for p in range(2,upTo): if isPrime[p]: yield p for m in range(p,upTo,p): isPrime[m]=False
Here is the most compact true sieve I have come up with so far. This performs surprisingly well.
def pgen(n): # Sieve of Eratosthenes generator
np = set() # keeps track of composite (not prime) numbers
for q in xrange(2, n+1):
if q not in np:
yield q
np.update(range(q*q, n+1, q))
>>> list(pgen(100))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
79, 83, 89, 97]
This slightly more complex version is the fastest I have seen:
def pgen(n): # Sieve of Eratosthenes generator by Dan Salmonsen
yield 2
np = set()
for q in xrange(3, n+1, 2):
if q not in np:
yield q
np.update(range(q*q, n+1, q+q))
Here is a true sieve as a list comprehension:
def primes(n):
sieve = set(sum([range(q*q, n+1, q+q) for q in odds], []))
return [2] + [p for p in odds if p not in sieve]
>>> primes(100)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
79, 83, 89, 97]
The following one-liner is not related at all to your code:
def primes(n):
return set(range(2,n))-{c for i in range(2,n) for c in range(2*i,n,i)}
Like your code, this is still not really the Sieve of Eratosthenes because, for example, it will futilely try to cross off multiples of 6 and 9 etc. Nevertheless it still runs significantly faster than most other Sieve look-alikes for values less than a million or more, since for small N there are "about as many" primes as non-primes (the fraction of numbers < N that are prime is 1/log(N)).
Heavily modified from source, possibly less efficient than original: http://codeblog.dhananjaynene.com/2011/06/10-python-one-liners-to-impress-your-friends/
Here's a simple demonstration of the sieve. Note that lambda isn't used as the filtering function, because the prime number needs to bound at definition time. Also of interest is that it's efficient in the sense of not duplicating divisions, but in the long run it could lead to you-know-what.
import itertools
def primes():
ints = itertools.count(2)
while True:
p = next(ints)
yield p
ints = itertools.ifilter(p.__rmod__, ints)
print list(itertools.islice(primes(), 10))
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
def sieve(n):
sieve_list = range(n)
zero_list = [0] * n
for i in range(2, int(n**.5) + 1):
if sieve_list[i]:
sieve_list[2*i:n:i] = zero_list[2*i:n:i]
return filter(None, sieve_list)[1:]
Not exactly the the most compact solution, but the step argument in the range function in Python3 helps here -
prime_sieve = [True] * (int(input('Primes Upto ?'))+1)
# The first prime number
for i in range(2, len(prime_sieve)):
if prime_sieve[i]:
for j in range(i+i, len(prime_sieve), i):
prime_sieve[j] = False
print(i, end=',')

What's the best way of skip N values of the iteration variable in Python?

In many languages we can do something like:
for (int i = 0; i < value; i++)
{
if (condition)
{
i += 10;
}
}
How can I do the same in Python? The following (of course) does not work:
for i in xrange(value):
if condition:
i += 10
I could do something like this:
i = 0
while i < value:
if condition:
i += 10
i += 1
but I'm wondering if there is a more elegant (pythonic?) way of doing this in Python.
Use continue.
for i in xrange(value):
if condition:
continue
If you want to force your iterable to skip forwards, you must call .next().
>>> iterable = iter(xrange(100))
>>> for i in iterable:
... if i % 10 == 0:
... [iterable.next() for x in range(10)]
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
As you can see, this is disgusting.
Create the iterable before the loop.
Skip one by using next on the iterator
it = iter(xrange(value))
for i in it:
if condition:
i = next(it)
Skip many by using itertools or recipes based on ideas from itertools.
itertools.dropwhile()
it = iter(xrange(value))
for i in it:
if x<5:
i = dropwhile(lambda x: x<5, it)
Take a read through the itertools page, it shows some very common uses of working with iterators.
itertools islice
it = islice(xrange(value), 10)
for i in it:
...do stuff with i...
It's a very old question, but I find the accepted answer is not totally stisfactory:
first, after the if ... / [next()...] sequence, the value of i hasn't changed. In your first example, it has.
second, the list comprehension is used to produce a side-effect. This should be avoided.
third, there might be a faster way to achieve this.
Using a modified version of consume in itertools recipes, you can write:
import itertools
def consume(it, n):
return next(itertools.islice(it, n-1, n), None)
it = iter(range(20))
for i in it:
print(i, end='->')
if i%4 == 0:
i = consume(it, 5)
print(i)
As written in the doctstring of consume, the iterator is consumed at C speed (didn't benchmark though). Output:
0->5
6->6
7->7
8->13
14->14
15->15
16->None
With a minor modification, one can get 21 instead of None, but I think this isnot a good idea because this code does work with any iterable (otherwise one would prefer the while version):
import string
it = iter(string.ascii_lowercase) # a-z
for x in it:
print(x, end="->")
if x in set('aeiouy'):
x = consume(it, 2) # skip the two letters after the vowel
print(x)
Output:
a->c
d->d
e->g
h->h
i->k
l->l
m->m
n->n
o->q
r->r
s->s
t->t
u->w
x->x
y->None
Itertools has a recommended way to do this: https://docs.python.org/3.7/library/itertools.html#itertools-recipes
import collections
def tail(n, iterable):
"Return an iterator over the last n items"
# tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n))
Now you can do:
for i in tail(5, range(10)):
print(i)
to get
5
6
7
8
9
I am hoping I am not answering this wrong... but this is the simplest way I have come across:
for x in range(0,10,2):
print x
output should be something like this:
0
2
4
6
8
The 2 in the range parameter's is the jump value
Does a generator function here is rebundant?
Like this:
def filterRange(range, condition):
x = 0
while x < range:
x = (x+10) if condition(x) else (x + 1)
yield x
if __name__ == "__main__":
for i in filterRange(100, lambda x: x > 2):
print i
There are a few ways to create iterators, but the custom iterator class is the most extensible:
class skip_if: # skip_if(object) for python2
"""
iterates through iterable, calling skipper with each value
if skipper returns a positive integer, that many values are
skipped
"""
def __init__(self, iterable, skipper):
self.it = iter(iterable)
self.skip = skipper
def __iter__(self):
return self
def __next__(self): # def next(self): for python2
value = next(self.it)
for _ in range(self.skip(value)):
next(self.it, None)
return value
and in use:
>>> for i in skip_if(range(1,100), lambda n: 10 if not n%10 else 0):
... print(i, end=', ')
...
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
I think you have to use a while loop for this...for loop loops over an iterable..and you cannot skip next item like how you want to do it here

Categories

Resources