Python get the last element from generator items - python

I'm super amazed using the generator instead of list.
But I can't find any solution for this question.
What is the efficient way to get the first and last element from generator items?
Because with list we can just do lst[0] and lst[-1]
Thanks for the help. I can't provide any codes since it's clearly that's just what I want to know :)

You have to iterate through the whole thing. Say you have this generator:
def foo():
yield 0
yield 1
yield 2
yield 3
The easiest way to get the first and last value would be to convert the generator into a list. Then access the values using list lookups.
data = list(foo())
print(data[0], data[-1])
If you want to avoid creating a container, you could use a for-loop to exhaust the generator.
gen = foo()
first = last = next(gen)
for last in gen: pass
print(first, last)
Note: You'll want to special case this when there are no values produced by the generator.

Related

Printing values in a formatted way that has different formats depending on value?

I'm still relatively new to python and was just wondering how to print values from a function that takes in a list as input and prints every value with a comma separated by every two values except -1 which it just prints by itself (assuming there will always be two values matched together if its not -1).
Some examples would be:
input: [2,3,4,2,-1,4,3]
output: 2 3, 4 2, -1, 4 3
input: [2,1,-1]
output: 2 1, -1
Every time a solution it feels like I'm overthinking it with while loops and if statements. Is there anyway for this to be faster and easier?
For situations where you may need to take multiple elements from a list in one iteration, an iterator is often a viable solution. Calling the built-in iter() on any iterable object (lists, strings, dicts, generators) will provide an iterator, which returns objects one at a time, dynamically, and which cannot backtrack. If you then assign an iterator to a variable and use that variable in a for loop, you can selectively call next() on it yourself to make the loop 'skip' elements:
inp = [2,3,4,2,-1,4,3]
inp_iter = iter(inp)
output = []
for elem in inp_iter: # each iteration essentially calls next() on the iterator until there is no more next()
if elem == -1:
output.append(str(elem))
else:
# withdraw the next element from the iterator before the `for` loop does automatically
# thus, the for loop will skip this element
next_elem = next(inp_iter)
output.append(f"{elem} {next_elem}")
print(', '.join(output))
# '2 3, 4 2, -1, 4 3'
You'll need to add error handling to this to deal with edge cases, but this should handle your immediate problem.

Is it possible to return the elements of a list one by one every time the code is ran?

I would like to do something along the lines of what random.choice(range(12)) does, but instead of returning one random choice every time the code is ran, I would like one element whenever the code is ran, but in increasing order. Is this possible?
That's what iterators are for:
>>> valuemaker = iter(range(12)) # Or any other iterable you want to statefully pull a value at a time from
>>> print(next(valuemaker))
0
>>> print(next(valuemaker))
1
>>> print(next(valuemaker))
2
etc. The iterator itself is stateful, so each time you call next on it it resumes from the last point and produces the next value. Obviously if you want to pull them all one by one in a loop you could just use a for loop (and skip calling iter, since the for does it implicitly). If you want the values in random order, just call random.shuffle on the list before creating the iterator.
Trivially:
def get_next():
for item in [1, 2, 17, 42]:
yield item

not able to use append function with list in python [duplicate]

I am trying to append objects to the end of a list repeatedly, like so:
list1 = []
n = 3
for i in range(0, n):
list1 = list1.append([i])
But I get an error like: AttributeError: 'NoneType' object has no attribute 'append'. Is this because list1 starts off as an empty list? How do I fix this error?
This question is specifically about how to fix the problem and append to the list correctly. In the original code, the reported error occurs when using a loop because .append returns None the first time. For why None is returned (the underlying design decision), see Why do these list operations return None, rather than the resulting list?.
If you have an IndexError from trying to assign to an index just past the end of a list - that doesn't work; you need the .append method instead. For more information, see Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add elements to a list?.
If you want to append the same value multiple times, see Python: Append item to list N times.
append actually changes the list. Also, it takes an item, not a list. Hence, all you need is
for i in range(n):
list1.append(i)
(By the way, note that you can use range(n), in this case.)
I assume your actual use is more complicated, but you may be able to use a list comprehension, which is more pythonic for this:
list1 = [i for i in range(n)]
Or, in this case, in Python 2.x range(n) in fact creates the list that you want already, although in Python 3.x, you need list(range(n)).
You don't need the assignment operator. append returns None.
append returns None, so at the second iteration you are calling method append of NoneType. Just remove the assignment:
for i in range(0, n):
list1.append([i])
Mikola has the right answer but a little more explanation. It will run the first time, but because append returns None, after the first iteration of the for loop, your assignment will cause list1 to equal None and therefore the error is thrown on the second iteration.
I personally prefer the + operator than append:
for i in range(0, n):
list1 += [[i]]
But this is creating a new list every time, so might not be the best if performance is critical.
Note that you also can use insert in order to put number into the required position within list:
initList = [1,2,3,4,5]
initList.insert(2, 10) # insert(pos, val) => initList = [1,2,10,3,4,5]
And also note that in python you can always get a list length using method len()
Like Mikola said, append() returns a void, so every iteration you're setting list1 to a nonetype because append is returning a nonetype. On the next iteration, list1 is null so you're trying to call the append method of a null. Nulls don't have methods, hence your error.
use my_list.append(...)
and do not use and other list to append as list are mutable.

How to join multiple rows into single line using python? [duplicate]

I am trying to append objects to the end of a list repeatedly, like so:
list1 = []
n = 3
for i in range(0, n):
list1 = list1.append([i])
But I get an error like: AttributeError: 'NoneType' object has no attribute 'append'. Is this because list1 starts off as an empty list? How do I fix this error?
This question is specifically about how to fix the problem and append to the list correctly. In the original code, the reported error occurs when using a loop because .append returns None the first time. For why None is returned (the underlying design decision), see Why do these list operations return None, rather than the resulting list?.
If you have an IndexError from trying to assign to an index just past the end of a list - that doesn't work; you need the .append method instead. For more information, see Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add elements to a list?.
If you want to append the same value multiple times, see Python: Append item to list N times.
append actually changes the list. Also, it takes an item, not a list. Hence, all you need is
for i in range(n):
list1.append(i)
(By the way, note that you can use range(n), in this case.)
I assume your actual use is more complicated, but you may be able to use a list comprehension, which is more pythonic for this:
list1 = [i for i in range(n)]
Or, in this case, in Python 2.x range(n) in fact creates the list that you want already, although in Python 3.x, you need list(range(n)).
You don't need the assignment operator. append returns None.
append returns None, so at the second iteration you are calling method append of NoneType. Just remove the assignment:
for i in range(0, n):
list1.append([i])
Mikola has the right answer but a little more explanation. It will run the first time, but because append returns None, after the first iteration of the for loop, your assignment will cause list1 to equal None and therefore the error is thrown on the second iteration.
I personally prefer the + operator than append:
for i in range(0, n):
list1 += [[i]]
But this is creating a new list every time, so might not be the best if performance is critical.
Note that you also can use insert in order to put number into the required position within list:
initList = [1,2,3,4,5]
initList.insert(2, 10) # insert(pos, val) => initList = [1,2,10,3,4,5]
And also note that in python you can always get a list length using method len()
Like Mikola said, append() returns a void, so every iteration you're setting list1 to a nonetype because append is returning a nonetype. On the next iteration, list1 is null so you're trying to call the append method of a null. Nulls don't have methods, hence your error.
use my_list.append(...)
and do not use and other list to append as list are mutable.

Indexing form the end of a generator

Say I have a generator in Python and I want to iterate over everything in it except the first 10 iterations and the last 10 iterations. itertools.islice supports the first part of this slicing operation, but not the second. Is there a simple way to accomplish this?
Something like this might do the job. EDIT: Added use of deque as per comments.
from collections import deque
def generator():
for i in ['ignore'] * 10 + ['yield this'] * 10 + ['ignore'] * 10:
yield i
def func(mygenerator):
cache = deque()
for i, item in enumerate(mygenerator()):
if i < 10:
continue
cache.appendleft(item)
if len(cache) > 10:
yield cache.pop()
for i in func(generator):
print i
Not only is there not a simple way, there is not a way at all, if you want to allow any generator (or any iterable). In general, there is no way to know when you are 10 items from the end of a generator, or even whether the generator has an end. Generators only give you one item at a time, and tell you nothing about how many items are "left". You would have to iterate through the entire generator, keeping a temporary cache of the most recent 10 items, and then yield those when (or if!) the generator terminates.
Note the "or if". A generator need not be finite. For an infinite generator, there is no such thing as the "last" 10 elements.

Categories