Why is using a while loop better? - python

I am making a program that uses a loop to run forever and I have a snippet of code below to show you how I acheive the loop. This is just an example and not the actual program. It is the same idea though.
import time
def start():
print "hello"
time.sleep(0.2)
start()
start()
All of my programmer friends tell me not to do this, and use a while loop instead. Like this:
import time
def start():
while True:
print "Hello"
time.sleep(0.2)
start()
Why should I use the while loop instead when both methods work perfectly fine?

Each time you are recursing, you are pushing a frame context onto your program stack. Soon you would have used up your entire allotted stack space causing stackoverflow, no pun intended ;)
The second approach has no such flaws. Hence by the looks of it 2nd is better than 1st approach (unless more of the program is presented).

If you run the program continuously with the recursion, you will get RuntimeError:
Traceback (most recent call last):
File "t.py", line 8, in <module>
start()
File "t.py", line 7, in start
start()
File "t.py", line 7, in start
start()
...
File "t.py", line 7, in start
start()
RuntimeError: maximum recursion depth exceeded
>>> import sys
>>> sys.getrecursionlimit()
1000

the while loop amd recursion both have their advantages and disadvantages but while loop is just a test and conditional jump. wheras ecursion involves pushing a stack frame, jumping, returning, and popping back from the stack.So they might have preferred while loop :)

Python does not have Tail Call Optimization (TCO) and cannot elide the stack. As such, recursion should not be used for an unbound depth (in languages without TCO) lest you get a stack-overflow error!
While the current code might appear to be fine, consider this simple change which will quickly reveal a problem.
def start():
print "hello"
# let's go faster! -- time.sleep(0.2)
start()
start()

Related

Is there a known issue with Python's Threading and waiting a long time?

Sorry if this is a strange or not completely detailed question, but I have a music app, and when you pause music, then resume a long time later the app will occasionally crash. It is not consistent, around 15% of the time it'll crash, and I have not found a common cause for this. The app will crash with a:
Exception in thread Thread-46 (resume):
Traceback (most recent call last):
File "C:\Users\my_name\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1009, in _bootstrap_inner
Process finished with exit code -1073741819 (0xC0000005)
The initial call calls a fuction:
def resume_thread(self):
self.is_playing = True
self.thr = KThread(target=self.resume)
self.thr.start()
self.song_begin = time.time()
It basically calls 'resume' on a thread, the other two variables are just for keeping track of internal stuff, not important. self.resume looks like:
def resume(self):
self.exited.clear()
self.resume_list.append(self.pause_time)
self.song_count -= 1
new_time = 0
self.controller.music_obj = self.current_playlist[self.song_count]
self.song = AudioSegment.from_file(self.controller.music_obj.Savelocation)
for true_time in self.resume_list:
new_time += true_time
self.song = self.song[new_time:]
self.coming_from_loop = False
self.testsong("sent")
So this function (top to bottom): clears a class variable that holds a threading.Event(). Calculates how much time is left in the song. Corrects the song index. Sends the current song to the 'controller' (MVC model). Adds up all of the time that has been paused, removes that time from the current song (so amount of song that has been played already). sets classs var (coming_from_loop (not important)), and finally, calls 'testsong', which plays the music. I have also noticed that there are two main things affecting the odds of a crash:
1). A module I use to use 'global keybinds'. Called 'system_hotkey'. I noticed that the app will have a much higher crash chance if this keybind is used to call 'pause' versus manually clicking a button (in tkinter). The app has crashed before without using it, but it produced a slightly different error that I have forgotten to document. I'll add it if I manage to reproduce it.
2). Change in how the 'time elapsed' is calculated. Before, it was a system based on the time between playing & pausing. So if you pause, then resumed 10 minutes later, it would've used that to calculate how much of the song is left. Now, it is a timer that counts down while playing music, and pauses when the song is paused. This significantly reduced how often the crash occured
3). Only ever occurs when it has been a long time elapsed since pause. Has never occurred within a smaller timeframe like 30 seconds.
I can't say for sure what causes it, as it is fairly hard to reproduce, but it seems to occur because the thread is trying to do a calculation after waiting a while (like creating a Pydub.Audiosegment), during which, it fails to do so and crashes. If you want to view all of the source code, all of it is in: https://github.com/nednoodlehead/punge/blob/master/punge_main.py Line 433-896.
Any help or thoughts are greatly appreciated.
Solution ended up being something off screen. Where the collective pause-time of one track was longer than the next track, that data would 'leak' over and try to begin the music at after it's total length. Guess it didnt audibly become apparent because it would overwrite that audiosegment with a new one, but if it failed to write the first one, the crash would occur.

Using schedule module to reming me to drink water every ten seconds

I am using schedule module to remind me to drink water every ten seconds
import schedule
def remindDrink():
print("Drink Water")
while True:
schedule.every().day.at("16:35").do(remindDrink())
So the problem here is that the task gets executed, but immedieately, not at the given time, and VSCode throws a weird error at me
Traceback (most recent call last):
File "e:\Code\Python Code\randomModule.py", line 12, in <module>
schedule.every().day.at("16:31").do(sendNotification())
File "C:\Users\PC\AppData\Local\Programs\Python\Python310\lib\site-packages\schedule\__init__.py", line 625, in do
self.job_func = functools.partial(job_func, *args, **kwargs)
TypeError: the first argument must be callable
PS E:\Code\Python Code>
This is the error, what am I doing wrong?
Same module different approach, I personally prefer this approach because it keeps my work clean, easy to read and to understand at your first glance and ofcourse easy to refactor.
from schedule import every, repeat, run_pending
import time
#repeat(every().day.at("16:35"))
def remindDrink():
print("Drink Water")
while True:
run_pending()
time.sleep(1)
Your broken code fixed:
Your broken code is fixed below, now the choice is yours, you can either use the above code or this:
import schedule
import time
def remindDrink():
print("Drink Water")
schedule.every().day.at("16:35").do(remindDrink)
while True:
schedule.run_pending()
time.sleep(1)
Remove the () from remindDrink() in the last line inside the do() function
Your code should look like this:
schedule.every().day.at("16:35").do(remindDrink)
Refer back to this question: TypeError: the first argument must be callable in scheduler library
quick thought, shedule....do(), within do() you don't run the function, just put the name of the function inside do.
'''
schedule.every().day.at("16:35").do(remindDrink)
'''

Python - Looping through functions is throwing errors in bg, until maximum recursion depth exceeded

So I think I am just fundamentally doing something wrong, but here is a basic example of what I am doing
some variables here
some code here to run once
def runfirst():
do some stuff
runsecond()
def runsecond():
do some different stuff
runthird():
def runthird():
do some more stuff
runfirst():
runfirst()
So it basically pulls some info I need at beginning, and then runs through 3 different variables. What I am doing is pulling info from db, then watching some counts on the db, and if any of those counts goes over a certain number over a time period, it sends email. This is for monitoring purposes, and I need it to run all the time. The problem I get is, all the time it is running, in the background it is throwing errors like "File "asdf.py", line blah, in firstrun"
I think it is complaining because it sees that I am looping through functions, but for what I need this to do, it works perfectly, except for the errors, and eventually killing my script due to maximum recursion depth exceeded. Any help?
You have infinite recursion here. Because you call runfirst from runthird, it keeps going deeper and deeper and none of the functions ever return. You might want to consider putting the functions in a while True loop instead of calling them from each other.
def runfirst():
do some stuff
def runsecond():
do some different stuff
def runthird():
do some more stuff
while True:
runfirst()
runsecond()
runthird()
You're not looping.
You're calling a function that calls another function that calls a third function that calls the first function which calls the second function which calls the third function which again calls the first function...and so on until your stack overflows.

Error while running 'classloop'

i was messing around with classes and thought i could try and make a class just loop
here is what i did:
class A():
def __init__(self):
print 1
self.loop()
def loop(self):
print 2
self.__init__()
A()
it prints 1 & 2 back and fourth for a while then i get a error that starts looping that looks like this:
Traceback (most recent call last):
File "C:/Python27/classloop.py", line 12, in <module>
A()
then it starts looping these two errors back and fourth:
File "C:/Python27/classloop.py", line 4, in __init__
self.loop()
File "C:/Python27/classloop.py", line 9, in loop
self.__init__()
just wondering why this happens all of the sudden why doesnt it just keep looping?
Every function call creates a stack frame, and every return pops a frame off the stack. This means that if you recurse too deep without returning, Python will run out of room on the stack and throw an exception. You can increase the limit, but most of the time, this will only make your program run a bit longer before crashing, or worse, the Python interpreter will corrupt its memory and go crazy.
In python there is a maximum recursion limit.
The default is 1000.
You can see that by typing:
import sys
print sys.getrecursionlimit()
in the terminal.
If you want to increase it use:
sys.setrecursionlimit(10000) # 10000 is just an example

When while loop placed in wxPython events

I'm trying to write a GUI program grabbing specific contents from a webpage. The idea is when I hit the start button, the program should start extracting information from that page. And I want to add some code to check if connected to the Internet. If not, continue trying until connected.
So I just added the following code in the event, but found it didn't work. Also the whole program has to be closed in a forced way. Here's my code:
import urllib2
import time
InternetNotOn = True
while InternetNotOn:
try:
urllib2.urlopen("http://google.com")
InternetNotOn = False
print "Everyting is fine!"
except urllib2.URLError, e:
print "Error!"
time.sleep(10)
What could the problem be?
When you have an event based program, the overall flow of the program is this:
while the-program-is-running:
wait-for-an-event
service-the-event
exit
Now, lets see what happens when service-the-event calls something with a (potentially) infinite loop:
while the-program-is-running:
wait-for-an-event
while the-internet-is-on:
do-something
exit
Do you see the problem? In the worse case your program may never call wait-for-an-event again because your loop is running.
Remember: the event loop is already an infinite loop, you don't need to add another infinite loop inside of it. Instead, take advantage of the existing loop. You can use wx.CallAfter or wx.CallLater to call a method which will cause your function to be called at the next iteration of the event loop.
Then, within your function you call wx.CallAfter or wx.CallLater again to cause it to again be called on the next iteration of the event loop.
Instead of time.sleep(10) you can call wxApp::Yield and time.sleep(1) ten times.
Beware of reentrancy problems (e.g. pressing the start button again.). The start button could be dimmed while in the event handler.
But Bryan Oakley's solution is probably the better way.

Categories