In the code I made a spreadsheet that records two random numbers every 10 seconds and add the numbers to three rows and when a certain condition was met it could end the loop but then I made it so that after the loop ends a new spreadsheet was created and the loop starts again. However when the loop starts again it adds extra rows with numbers to the sheet when I just want it to end at the same condition the first sheet ended at. I could not seem to figure out why I was getting extra rows filled with numbers
# global variable
globvar = 1
# start generate_random_number function
def generate_random_number():
int1 = random.randint(55, 99)
int2 = random.randint(1, 50)
print('Numbers have been generated')
return int1, int2
# end generate_random_number function
# start create_excel function
def create_excel():
# call global
global globvar
#check file created and not
if(globvar == 1):
wb = Workbook()
wb.remove_sheet(wb.get_sheet_by_name('Sheet'))
wb.create_sheet('Test '+str(globvar))
ws = wb.get_sheet_by_name('Test '+str(globvar))
ws['A1'] = 'Random 1'
ws['B1'] = 'Random 2'
wb.save('sample.xlsx')
print('Document created')
else:
wb = load_workbook('sample.xlsx')
wb.create_sheet('Test '+str(globvar))
ws = wb.get_sheet_by_name('Test '+str(globvar))
ws['A1'] = 'Random 1'
ws['B1'] = 'Random 2'
wb.save('sample.xlsx')
print('Document created')
# end create_excel function
# start update_excel function
def update_excel():
global globvar
wb = load_workbook('sample.xlsx')
ws = wb.get_sheet_by_name('Test '+str(globvar))
ws.append(generate_random_number())
wb.save('sample.xlsx')
print('Spreadsheet updated')
# end update_excel function
# start main function
def main():
global globvar
m = 1
create_excel()
update_excel()
schedule.every(10).seconds.do(update_excel)
loop = True
while loop:
schedule.run_pending()
m += 1
print('Waiting...')
time.sleep(10)
if m == 3:
schedule.run_pending()
loop = False
globvar +=1
#print('Finished program. ' + str(datetime.datetime.now()))
#print('Starting program now.... ' + str(datetime.datetime.now()))
#main()
# end main function
# starts whole script here
print('Starting program now.... '+ str(datetime.datetime.now()))
t = 1
while True:
main()
t += 1
if t == 3:
break
print('Finished program. '+ str(datetime.datetime.now()))
It's because more than 10 seconds have passed between increments of globvar. What will be happening is that you have 2 or more pending calls of update_excel running instead of 1 per increment.
That's not really surprising since you have a schedule of 10 seconds and a 10 second sleep meaning that any extra delays caused by code execution will make the loop take longer than 10 seconds and cause your scheduled tasks to queue.
Instead of using schedule.every just put your update_excel calls directly in your loop.
def main():
...
create_excel()
while loop:
update_excel()
time.sleep(10)
m += 1
if m == 3:
globvar += 1
return
Related
I'm struggling to understand this example from a PyCon talk (link to code example)
import threading, time, random
##########################################################################################
# Fuzzing is a technique for amplifying race condition errors to make them more visible
FUZZ = True
def fuzz():
if FUZZ:
time.sleep(random.random())
###########################################################################################
counter = 0
def worker():
'My job is to increment the counter and print the current count'
global counter
fuzz()
oldcnt = counter
fuzz()
counter = oldcnt + 1
fuzz()
print('The count is %d' % counter, end='')
fuzz()
print()
fuzz()
print('---------------', end='')
fuzz()
print()
fuzz()
print('Starting up')
fuzz()
for i in range(10):
threading.Thread(target=worker).start()
fuzz()
print('Finishing up')
fuzz()
The above would print something like:
Starting up
The count is 1The count is 2The count is 2The count is 2
---------------The count is 3
---------------The count is 4
---------------
---------------The count is 4
The count is 5------------------------------
Finishing up
The count is 5
------------------------------
The count is 6---------------
---------------
What I don't understand about this is why sometimes, when a thread is put to sleep it continues from where it left off and other times it stops and starts the next iteration.
EDIT:
I clearly missed the for loop...
All the threads run to completion. That's the point of the lesson. When multiple threads access the same variable simultaneously, you can get unexpected results.
I've modified the code slightly to print less distracting stuff and also a thread id. I think this should help clarify what is happening:
import threading, time, random
##########################################################################################
# Fuzzing is a technique for amplifying race condition errors to make them more visible
FUZZ = True
def fuzz():
if FUZZ:
delay = random.random()
time.sleep(delay)
return delay
###########################################################################################
counter = 0
def worker(id):
'My job is to increment the counter and print the current count'
global counter
fuzz()
oldcnt = counter
delay = fuzz()
counter = oldcnt + 1
print('%d> After %s seconds, the count is %d -> %d' % (id, delay, oldcnt, counter))
print('Starting up')
fuzz()
for i in range(10):
threading.Thread(target=worker, args=(i,)).start()
fuzz()
print('Finishing up')
fuzz()
Example output (because of the randomness, this will change each run):
Starting up
0> After 0.7350378311578315 seconds, the count is 0 -> 1
2> After 0.1785671063273092 seconds, the count is 1 -> 2
1> After 0.8294767498326181 seconds, the count is 0 -> 2
3> After 0.5901283670333175 seconds, the count is 1 -> 3
4> After 0.14964432977752684 seconds, the count is 2 -> 3
7> After 0.07459495416247286 seconds, the count is 2 -> 3
5> After 0.24680028896240813 seconds, the count is 1 -> 2
8> After 0.18685884099452443 seconds, the count is 3 -> 4
Finishing up
6> After 0.8512297712462166 seconds, the count is 2 -> 4
9> After 0.5959592400151894 seconds, the count is 4 -> 5
Because of the random sleep times between when the variable is stored (oldcnt = counter) and when it is incremented (counter = oldcnt + 1) you get the data race.
I am currently doing CS50 DNA pset and I wrote all of my code but it is slower for large files which results in check50 considering it wrong. I have attached my code and the error check50 shows below.
import sys
import csv
def main():
argc = len(sys.argv)
if (argc != 3):
print("Usage: python dna.py [database] [sequence]")
exit()
# Sets variable name for each argv argument
arg_database = sys.argv[1]
arg_sequence = sys.argv[2]
# Converts sequence csv file to string, and returns as thus
sequence = get_sequence(arg_sequence)
seq_len = len(sequence)
# Returns STR patterns as list
STR_array = return_STRs(arg_database)
STR_array_len = len(STR_array)
# Counts highest instance of consecutively reoccurring STRs
STR_values = STR_count(sequence, seq_len, STR_array, STR_array_len)
DNA_match(STR_values, arg_database, STR_array_len)
# Reads argv2 (sequence), and returns text within as a string
def get_sequence(arg_sequence):
with open(arg_sequence, 'r') as csv_sequence:
sequence = csv_sequence.read()
return sequence
# Reads STR headers from arg1 (database) and returns as list
def return_STRs(arg_database):
with open(arg_database, 'r') as csv_database:
database = csv.reader(csv_database)
STR_array = []
for row in database:
for column in row:
STR_array.append(column)
break
# Removes first column header (name)
del STR_array[0]
return STR_array
def STR_count(sequence, seq_len, STR_array, STR_array_len):
# Creates a list to store max recurrence values for each STR
STR_count_values = [0] * STR_array_len
# Temp value to store current count of STR recurrence
temp_value = 0
# Iterates over each STR in STR_array
for i in range(STR_array_len):
STR_len = len(STR_array[i])
# Iterates over each sequence element
for j in range(seq_len):
# Ensures it's still physically possible for STR to be present in sequence
while (seq_len - j >= STR_len):
# Gets sequence substring of length STR_len, starting from jth element
sub = sequence[j:(j + (STR_len))]
# Compares current substring to current STR
if (sub == STR_array[i]):
temp_value += 1
j += STR_len
else:
# Ensures current STR_count_value is highest
if (temp_value > STR_count_values[i]):
STR_count_values[i] = temp_value
# Resets temp_value to break count, and pushes j forward by 1
temp_value = 0
j += 1
i += 1
return STR_count_values
# Searches database file for DNA matches
def DNA_match(STR_values, arg_database, STR_array_len):
with open(arg_database, 'r') as csv_database:
database = csv.reader(csv_database)
name_array = [] * (STR_array_len + 1)
next(database)
# Iterates over one row of database at a time
for row in database:
name_array.clear()
# Copies entire row into name_array list
for column in row:
name_array.append(column)
# Converts name_array number strings to actual ints
for i in range(STR_array_len):
name_array[i + 1] = int(name_array[i + 1])
# Checks if a row's STR values match the sequence's values, prints the row name if match is found
match = 0
for i in range(0, STR_array_len, + 1):
if (name_array[i + 1] == STR_values[i]):
match += 1
if (match == STR_array_len):
print(name_array[0])
exit()
print("No match")
exit()
main()
Check50 error link:
https://submit.cs50.io/check50/fd890301a0dc9414cd29c2b4dcb27bd47e6d0a48
If you wait for long, then you get the answer but since my program is running slow check50 is considering it wrong
Well, I solved it just by adding a break statement.
I want to loop the end section of the code if you get the 'Error, class not found'. I already tried a, for and while loop but i couldn't get it working.
Health = 0
Damage = 0
Classes = ['Mage','Knight']
Class = ''
print('Select Character Name.')
Name = input()
print('Hello',Name,'please choose your class.')
print('')
print('Mage: 5 Health: 7 Damage')
print('Knight: 7 Health : 5 Damage')
#looped section
Select = input()
if Select in ['Mage']:
print('Mage Selected')
Health = 5
Damage = 7
Class = 'Mage'
elif Select in ['Knight']:
print('Knight Selected')
Health = 7
Damage = 5
Class = 'Knight'
else:
print('Error, class not found')
#loop back if this is outcome
when facing problems like this, your default approach should be a while loop making sure that the loop will go on (virtually forever) if the defined conditions aren't met. so this is what you can do:
while True:
# looped section
Select = input()
if Select in ['Mage']:
print('Mage Selected')
Health = 5
Damage = 7
Class = 'Mage'
break # break the loop if selection is correct
elif Select in ['Knight']:
print('Knight Selected')
Health = 7
Damage = 5
Class = 'Knight'
break # break the loop if selection is correct
else:
print('Error, class not found, try again: ') # will start the loop over
Perhaps an odd question but here it goes: I am trying to write an function which
take no arguments
return true if this function has been called 3 times or fewer in the last 1 second
return false otherwise
def myfunction():
myfunction.counter += 1
myfunction.counter = 0
This above code keeps track how many times this function is called but how to modify this so it satisfy above requirements?
I know that I can use time module in python but how to use it to solve this problem?
First keep track of when the function was called with a decorator:
import time
def counted(f):
def wrapped(*args, **kwargs):
wrapped.calls.append(int(round(time.time() * 1000))) # append the ms it was called
return f(*args, **kwargs)
wrapped.calls = []
return wrapped
This decorator can be used like so:
#counted
def foo():
print(2)
time.sleep(.3)
Then have a function to group the timestamps within a certain range:
def group_by(lst, seconds):
"""
Groups a list of ms times into the {seconds}
range it was called. Most recent grouping will
be in the first element of the list.
"""
ms = 1000 * seconds
result = []
if lst:
start = lst[-1]
count = 1
for ele in reversed(lst[:-1]):
if ele > start - ms:
count += 1
else:
result.append(count)
count = 1
start = ele
result.append(count)
return result
Finally test it:
for _ in range(5):
foo()
data = foo.calls
number_of_calls_last_second = group_by(data, 1)
print(f"foo called {number_of_calls_last_second[0]} times within the last second")
print(number_of_calls_last_second[0] <= 3) # Here is your True False output
Output:
2
2
2
2
2
foo called 4 times within the last second
False
I would use a decorator like this:
import time
def call_counter(calls_number, max_time):
def _decorator(function):
def helper():
helper.calls.append(time.time())
function()
if len(helper.calls) > calls_number:
helper.calls = helper.calls[calls_number:]
return time.time() - helper.calls[0] > max_time
return True
helper.calls = []
return helper
return _decorator
#call_counter(3, 1000)
def my_function():
pass
for _ in range(3):
print(my_function()) # Prints True three times
print(my_function()) # Prints False: the function has been called four times in less than one second.
time.sleep(1)
print(my_function()) # Prints True
I used parameters in the decorator so that you can reuse it with different values. If you have any question, ask me in the comments.
I have 3 different functions in a class if the command in any of the function returns false, I want the program to begin all over again the way it started if the function that returns false was the last command among the function. Also, I want the program to return to the last successful function command if the failed command is not the last among the functions
When I run the program and last function command fails, the program returned to the starting point but ignore certain and crucial part of what I want to achieve, rather it full execute the second function command
class Test():
def greeting(self):
user_input = input("Greeting: ")
print("This is the greeting function")
list1 = ["hello", "Hi"]
if user_input in list1:
print("Thats good")
Test.cities(self)
elif user_input not in list1:
print("Mtchewwww")
Test.greeting(self)
else:
print("be serious")
def cities(self):
print("U re now inside the city function")
list2 = ["Otukpo", "Gboko"]
user_input = input("Cities: ")
if user_input in list2:
print("Nice one")
Test.num(self)
else:
print("that's not a city")
Test.cities(self)
def num(self):
user_input = input("Numbers: ")
list3 = [1, 2, 3, 4]
if int(user_input) in list3:
print("good number")
elif user_input not in list3:
print("That was bad")
Test.greeting(self)
else:
print("Can not compute")
calling = Test()
cal = calling.greeting
cal()
I want the program to behave like this:
if item is in list1 move to the next function but if not in list, try the current function again 3 times and after the the 3 chances and it's still not in the list return to the previous function
def greeting():
user_input = input("Greeting: ")
# It is not good to name your variables like `list1`, `str2`...
# plus, if your list is immutable, use a tuple instead.
options = ('hello', 'Hi')
if user_input in options:
print("That's good")
return True
else:
return False
def cities():
user_input = input("Cities: ")
options = ("Otukpo", "Gboko")
if user_input in options:
print("Nice one")
return True
else:
print("That's not a city")
return False
def num():
user_input = input("Numbers: ")
options = (1, 2, 3, 4)
try:
if int(user_input) in options:
print("Good number")
return True
else:
return False
except ValueError:
# In case that the input is not a number
return False
def main():
fns = (greeting, cities, num)
ptr = 0
cnt = 0
# Just for your information, you don't have to use `ptr >= 0 and ptr <3` in python
while 0 <= ptr < 3:
# print(ptr, cnt)
if cnt >= 3:
# if failed for 3 times, move `ptr` to the previous function, and reset `cnt` to 0
ptr -= 1
cnt = 0
continue
# Get the current function, as `fn`
fn = fns[ptr]
if fn():
# if the current function is a success, move to next, and reset `cnt`
ptr += 1
cnt = 0
else:
# if the current function is a failure, it will be tried again in next while loop
cnt += 1
main()
Use a pointer ptr to iterate over your three functions, and use a variable cnt to limit the failure times. Just try it.
Have fun with python!