Python, for loop, not reseting the incrimination when calling the method - python

def seekNextStation(self):
counter = 0
print(counter)
for counter in range(len(self.stations)):
counter +=1
print(counter)
if counter != 6:
self.currentlyTuned = self.stations[counter]
counter +=1
print(counter, "in if")
else:
counter = 1
return "Currently Tuned: " + self.currentlyTuned
The part that i am trying to get is how do i keep the incrimination when i call seekNextStation(). at the moment it will change counter to 1 return the station in index[1] then change counter to 2, but when i call it again it will reset counter to 0 and redo the same steps

While you can rebind the index variable of the for loop, the result lasts until the start of the next iteration. Then Python rebinds it back to the next item from the sequence you passed to the for loop
It looks like you're trying to build a complicated way to cycle through stations. This type of thing is common enough to be included in the std library
>>> stations = ['station1', 'station2', 'station3', 'station4', 'station5', 'station6']
>>> from itertools import cycle
>>> station_gen = cycle(stations)
>>> next(station_gen)
'station1'
>>> next(station_gen)
'station2'
>>> next(station_gen)
'station3'
>>> next(station_gen)
'station4'
>>> next(station_gen)
'station5'
>>> next(station_gen)
'station6'
>>> next(station_gen)
'station1'
>>> next(station_gen)
'station2'
>>> next(station_gen)
'station3'
>>> next(station_gen)
'station4'

Related

How to check if generator was used?

Is it possible to know if generator was used? i.e.
def code_reader(code):
for c in code:
yield c
code_rdr = code_reader(my_code)
a = code_rdr.next()
foo(code_rdr)
After foo call I would like to know if .next() was called on code_rdr by foo or not.
Of course I could wrap it by some class with a counter for next() calls.
Is there any easy way to do so?
Python 3.2+ has inspect.getgeneratorstate(). So you can simply use inspect.getgeneratorstate(gen) == 'GEN_CREATED':
>>> import inspect
>>> gen = (i for i in range(3))
>>> inspect.getgeneratorstate(gen)
'GEN_CREATED'
>>> next(gen)
0
>>> inspect.getgeneratorstate(gen)
'GEN_SUSPENDED'
I have used idea from attached possible answers, bellow is redefined code_reader function:
def code_reader(code):
length = len(code)
i = 0
while i < length:
val = (yield i)
if val != 'position':
yield code[i]
i += 1
by using .send('position') on I would have know position of next item to be generated, i.e.
a = code_reader("foobar")
print a.next()
print a.send('position')
print a.next()
print a.send('position')
print a.send('position')
print a.next()
print a.send('position')
output:
0
0
f
1
1
o
2

IndexError: list assignment index out of range Python

def mode(given_list):
highest_list = []
highest = 0
index = 0
for x in range(0, len(given_list)):
occurrences = given_list.count(given_list[x])
if occurrences > highest:
highest = occurrences
highest_list[0] = given_list[x]
elif occurrences == highest:
highest_list.append(given_list[x])
The code is meant to work out the mode of a given list. I do not understand where I am going wrong.
Exact Error I am receiving.
line 30, in mode
highest_list[0] = given_list[x]
IndexError: list assignment index out of range
The problem is that you have an empty list originally:
highest_list = []
And then in the loop you try to access it at index 0:
highest_list[0] = ...
It's impossible, because it's an empty list and so is not indexable at position 0.
A better way to find the mode of a list is to use a collections.Counter object:
>>> from collections import Counter
>>> L = [1,2,3,3,4]
>>> counter = Counter(L)
>>> max(counter, key=counter.get)
3
>>> [(mode, n_occurrences)] = counter.most_common(1)
>>> mode, n_occurrences
(3, 2)
As far as getting the mode, you can just use a Counter from the collections library
from collections import Counter
x = [0, 1, 2, 0, 1, 0] #0 is the mode
g = Counter(x)
mode = max(g, key = lambda x: g[x])
At that point, at the start of the loop, highest_list is empty, so there's no first index. You can initialize highest_list as [0] so that there is always at least one "highest value."
That said, you can accomplish this more simply as follows:
def mode(given_list):
return max(set(given_list), key=given_list.count)
This will find the highest item in the passed given_list, based on each item's count() in it. Making a set first ensures that each item is only counted once.

Python: del in for loop

Problem:
test1 = [1, 2, 3]
for i in range(len(test1)): #Want to do the below for every element in test1
print(test1[i])
if test1[i] % 2 == 0: #if the element meets certain conditions, it gets deleted
del test1[i]
#The thing in test1 gets deleted, but the range gets not updated,
#and i iterates nonetheless, so 1 element gets ignored,
#and in the end, I get "list index out of range"
(In the actual code, there are far more if statements, and the things in the array are not integers but objects?! of a class, so list comprehension is more - meh...)
I've come up with this approach:
test1 = [1, 2, 3]
i = 0
x = len(test1)
while i < x:
print(test1[i])
if test1[i] % 2 == 0:
del test1[i]
x -= 1
i -= 1
i += 1
But as you can see, this is 5 lines longer (and for every del, I have to add 2 lines...), ugly, and annoying. Is there any nicer way to do something like that? Like, a redo command, that goes straight to the for loop, updates the range, and lets you chose i, or something other?
Use a list comprehension
Something along the lines of:
list = [x for x in list if x % 2 == 1] # Keep anything that is not even
for thing in list:
print(thing)
This will create a new list with anything that matches the conditional.
Then print the new list
>>> test1 = [1,2,3]
>>> list = [x for x in test1 if x % 2 is 1]
>>> print(list)
[1, 3]
Have you tried using the reversed on the range?
test1 = [1, 2, 3]
for i in reversed(range(len(test1))):
print(test1[i])
if test1[i] % 2 == 0:
del test1[i]
>>> 3
>>> 2
>>> 1
print(test1)
>>> [1, 3]
This way, when you delete an element, the array get reduced but since you are going through the list in a reversed order, only the index of already processed elements get affected.
EDIT
After reading all the comments, I decided to run some little benchmark. I created a list of 1,000 elements and a list of 10,000,000 elements and tried the 3 ways:
#1 Deleting from list
#2 Creating a new list with list comprehension
#3 Creating a new list with classic for
For the list of 1,000 elements, the time does not really matter but for the 10,000,000 elements list, deleting from the list get pretty much impossible (a couple of hours vs half a second to create a new list).
Test with 1,000 element
>>> Test 0, 11th element: 7e-06 seconds
>>> Test 0, second last element: 2e-06 seconds
>>> Test 1: 0.00017 seconds
>>> Test 2: 0.000103 seconds
>>> Test 3: 0.000234 seconds
Test with 10,000,000 element
>>> Test 0, 11th element: 0.011888 seconds
>>> Test 0, second last element: 4e-06 seconds
>>> Test 1: Too long!!
>>> Test 2: 0.941158 seconds
>>> Test 3: 0.681262 seconds
In regards of this, I would strongly suggest creating a new list with the list comprehension or with a regular for loop.
Here is my code:
from datetime import datetime
from random import randint
# Create my test lists of 10 000 000 elements
test_0 = []
#nb_el = 10000000
nb_el = 1000
while (len(test_0) < nb_el):
test_0.append(randint(0, 100))
test_1 = test_0[:] # clone list1
test_2 = test_0[:] # clone list1
test_3 = test_0[:] # clone list1
# Test #0
# Remove the 11th element and second last element
d1 = datetime.now()
del test_0[10]
d2 = datetime.now()
print('Test 0, 11th element: ' +
str((d2-d1).microseconds / 1000000.0) + ' seconds')
d1 = datetime.now()
del test_0[nb_el-2]
d2 = datetime.now()
print('Test 0, second last element: '
+ str((d2-d1).microseconds / 1000000.0) + ' seconds')
# Test #1 | TOO SLOW TO BE RAN with 10 000 000 elements
# Delete every element where element is a multiple of 2
d1 = datetime.now()
for i in reversed(range(len(test_1))):
if test_1[i] % 2 == 0:
del test_1[i]
d2 = datetime.now()
print('Test 1: ' + str((d2-d1).microseconds / 1000000.0) + ' seconds')
# Test #2
# Create a new list with every element multiple of 2
# using list comprehension
d1 = datetime.now()
test_2_new = [x for x in test_2 if x % 2 == 0]
d2 = datetime.now()
print('Test 2: ' + str((d2-d1).microseconds / 1000000.0) + ' seconds')
# Test #3
# Create a new list with every element multiple of 2
# using for loop
d1 = datetime.now()
test_3_new = []
for i in range(len(test_3)):
if test_3[i] % 2 == 0:
test_3_new.append(test_3[i])
d2 = datetime.now()
print('Test 3: ' + str((d2-d1).microseconds / 1000000.0) + ' seconds')
Assuming you want to print every number before removing them from the list.
myList = [1,2,3]
for n in test1:
print n
test1 = [n for n in myList if n % 2] # 0 = False by default in python. No need for == 0 / > 0.
if you absulutely want to use the code you wrote as it may be easier for you to understand:
test1 = [1, 2, 3]
for i in range(len(test1)): #Want to do the below for every element in test1
print(test1[i])
# Either check if the index is not greather than the length of the list.
if test1[i] % 2 == 0 and i <= len(test1): #if the element meets certain conditions, it gets deleted
# Or use try, except (Will be slow if your list is great in size as there will be many exceptions caught.
if test1[i] % 2 == 0:
try:
del test1[i]
except:
continue
#The thing in test1 gets deleted, but the range gets not updated,
#and i iterates nonetheless, so 1 element gets ignored,
#and in the end, I get "list index out of range"
Modifying an iterable while you're iterating over it can get you into lots of trouble. Try the following instead:
test1 = [1,2,3]
test2 = []
for i in test1:
print(i)
if i%2:
test2.append(i)
Then you have test2 as the finished version.
This could be written more compactly with a comprehension:
test1 = [1,2,3]
test2 = [i for i in test1 if i%2]
print(*test2, sep='\n')

Why does Python modify the list which is out of the loop?

for this list:
current_trace = [[3,5,1,5,7,9,4]]
I run the sliding_tristep() method, which includes the predict() and window() methods:
def predict(lst):
print "predicting for", lst
print "result", max(lst) + 0.0
return max(lst) + 0.0
def window(lst, n=3):
for x in range(1, len(lst)+1): # or len(l)+n to continue till the end
yield(lst[max(0, x-n):x])
def sliding_tristep(full_trace, future_step = 2, window_size = 3):
for user_trace in full_trace:
for current_input in window(user_trace):
counter = 0
trace = current_input
accumulator = []
while counter <= future_step:
next_prediction = predict(trace)
trace.append(next_prediction)
accumulator.append(next_prediction)
trace = trace[-window_size:]
counter += 1
print current_input, accumulator
When I run sliding_tristep(current_trace), in the output for the print current_input, accumulator line I notice that the current_input has been modified although it is out of the while loop which makes the calculations in sliding_tristep(current_trace).
I wonder why does this happen? How is that possible for python to modify a list which is not used at all in the subsequent loop.
I run sliding_tristep(current_trace), in the output for the print current_input, accumulator line I notice that the current_trace has been modified
just tested your code:
>>> current_trace = [[3,5,1,5,7,9,4]]
>>> sliding_tristep(current_trace)
...
>>> current_trace
[[3, 5, 1, 5, 7, 9, 4]]
current_trace does not get modified.
I wonder why does this happen? How is that possible for python to modify a list which is not used at all in the subsequent loop.
Though, I guess you meant current_input and not current_trace.
current_input gets modified, because trace is a reference to current_input and trace gets modified.
If you want to make a copy of current_input as trace, here's one way to do it:
>>> foo = [1,2,3]
>>> bar = foo[:]
>>> bar.append(4)
>>> foo
[1, 2, 3]
>>> bar
[1, 2, 3, 4]
applied to your code:
def sliding_tristep(full_trace, future_step = 2, window_size = 3):
for user_trace in full_trace:
for current_input in window(user_trace):
counter = 0
trace = current_input[:] # make a copy of current_input
accumulator = []
while counter <= future_step:
next_prediction = predict(trace)
trace.append(next_prediction)
accumulator.append(next_prediction)
trace = trace[-window_size:]
counter += 1
print current_input, accumulator
If you don't modify the elements of the list (and as integers are non-mutable, you can't), you can do the shallow copy as I'm suggesting you in the aforementioned example. If you're using mutable objects (lists or other kind of objects), then you want to do a deep copy using the copy module. Look at this answer: https://stackoverflow.com/a/184660/1290438 on this topic.
using trace.extend(current_input) , instead of trace = current_input solves the problem. although, a trace list has to be initialized beforehand.
The solution would look like:
def sliding_tristep(full_trace, future_step = 2, window_size = 3):
for user_trace in full_trace:
for current_input in window(user_trace):
counter = 0
trace = [] #here is the solution
trace.extend(current_input) #here is the solution
accumulator = []
while counter <= future_step:
next_prediction = predict(trace)
trace.append(next_prediction)
accumulator.append(next_prediction)
trace = trace[-window_size:]
counter += 1

Python: Find X to Y in a list of strings

I have a list of maybe a 100 or so elements that is actually an email with each line as an element. The list is slightly variable because lines that have a \n in them are put in a separate element so I can't simply slice using fixed values. I essentially need a variable start and stop phrase (needs to be a partial search as well because one of my start phrases might actually be Total Cost: $13.43 so I would just use Total Cost:.) Same thing with the end phrase. I also do not wish to include the start/stop phrases in the returned list. In summary:
>>> email = ['apples','bananas','cats','dogs','elephants','fish','gee']
>>> start = 'ban'
>>> stop = 'ele'
# the magic here
>>> print new_email
['cats', 'dogs']
NOTES
While not perfect formatting of the email, it is fairly consistent so there is a slim chance a start/stop phrase will occur more than once.
There are also no blank elements.
SOLUTION
Just for funzies and thanks to everybody's help here is my final code:
def get_elements_positions(stringList=list(), startPhrase=None, stopPhrase=None):
elementPositionStart, elementPositionStop = 0, -1
if startPhrase:
elementPositionStart = next((i for i, j in enumerate(stringList) if j.startswith(startPhrase)), 0)
if stopPhrase:
elementPositionStop = next((i for i, j in enumerate(stringList) if j.startswith(stopPhrase)), -1)
if elementPositionStart + 1 == elementPositionStop - 1:
return elementPositionStart + 1
else:
return [elementPositionStart, elementPositionStop]
It returns a list with the starting and ending element position and defaults to 0 and -1 if the respective value cannot be found. (0 being the first element and -1 being the last).
SOLUTION-B
I made a small change, now if the list is describing a start and stop position resulting in just 1 element between it returns that elements position as an integer instead of a list which you still get for multi-line returns.
Thanks again!
>>> email = ['apples','bananas','cats','dogs','elephants','fish','gee']
>>> start, stop = 'ban', 'ele'
>>> ind_s = next(i for i, j in enumerate(email) if j.startswith(start))
>>> ind_e = next(i for i, j in enumerate(email) if j.startswith(stop) and i > ind_s)
>>> email[ind_s+1:ind_e]
['cats', 'dogs']
To satisfy conditions when element might not be in the list:
>>> def get_ind(prefix, prev=-1):
it = (i for i, j in enumerate(email) if i > prev and j.startswith(prefix))
return next(it, None)
>>> start = get_ind('ban')
>>> start = -1 if start is None else start
>>> stop = get_ind('ele', start)
>>> email[start+1:stop]
['cats', 'dogs']
An itertools based approach:
import itertools
email = ['apples','bananas','cats','dogs','elephants','fish','gee']
start, stop = 'ban', 'ele'
findstart = itertools.dropwhile(lambda item: not item.startswith(start), email)
findstop = itertools.takewhile(lambda item: not item.startswith(stop), findstart)
print list(findstop)[1:]
// ['cats', 'dogs']
Here you go:
>>> email = ['apples','bananas','cats','dogs','elephants','fish','gee']
>>> start = 'ban'
>>> stop = 'ele'
>>> out = []
>>> appending = False
>>> for item in email:
... if appending:
... if stop in item:
... out.append(item)
... break
... else:
... out.append(item)
... elif start in item:
... out.append(item)
... appending = True
...
>>> out.pop(0)
'bananas'
>>> out.pop()
'elephants'
>>> print out
['cats', 'dogs']
I think my version is much more readable than the other answers and doesn't require any imports =)

Categories