run code after yield statement in python generator when using break - python

I have a simple generator:
def counter(n):
counter = 0
while counter <= n:
counter += 1
yield counter
print(f"Nice! You counted to {counter}")
def test_counter():
for count in counter(6):
print(f"count={count}")
if count > 3:
break
In this example there is a break once count reaches 3. However the only problem with the code is that the break terminates the generator execution at the yield statement. It doesn't run the print statement after the yield statement. Is there a way to execute code within the generator that is after the yield statement when a break occurs?
Example run:
# What happens:
>>> test_counter()
count=1
count=2
count=3
count=4
# What I'd like to see:
>>> test_counter()
count=1
count=2
count=3
count=4
Nice! You counted to 4

You have to put the print in a finally-block:
def counter(n):
try:
counter = 0
while counter <= n:
counter += 1
yield counter
finally:
print(f"Nice! You counted to {counter}")
def test_counter():
for count in counter(6):
print(f"count={count}")
if count > 3:
break

You can use send method of generator:
def counter(n):
counter = 0
while counter <= n:
counter += 1
should_continue = yield counter
if should_continue == False:
break
print(f"Nice! You counted to {counter}")
def test_counter():
gen = counter(6)
for count in gen:
print(f"count={count}")
if count > 3:
try:
gen.send(False)
except StopIteration:
break
Now test_counter() works as expected.

Related

Return main function in sub function

I create multiple sub functions to process data and want to return the main function, once any nested function meets some condition. Then, the multiprocessing can continue the processes.
Here's a simple example:
import multiprocessing
from multiprocessing import Pool
def program1(num):
num += 1
return num
def program2(num):
num += 1
return num
def program3(num):
num += 1
return num
def main(num):
num = program1(num)
if num %2 != 0:
return
else:
num = program2(num)
if num %3 != 0:
return
else:
num = program3(num)
print(num)
return num
pool = Pool(4)
nums = range(8)
pool.map(main, nums)
Because there're multiple programs, it will make the code looks quite long ...
Is it possible to wrap the if condition in each nested function?
Then I can just use
def main(num):
num = program1(num)
num = program2(num)
num = program3(num)
Note that please don't care or simplify the data process hah. This is just an example to show the processes.

Resetting Collatz Counter on Each New Recursion

I made a code that measure number of steps it takes to return to 1 in a Collatz Conjecture. Here Is my code
counter = 0
def collatz(n):
global counter
counter += 1
if n <= 0 :
return "Invalid Number"
elif n == 1 :
return counter
elif n % 2 == 1 :
n = 3*n + 1
return collatz(n)
elif n % 2 == 0 :
n = n/2
return collatz(n)
print(collatz(9921615699))
print(collatz(9921615699))
I expect the Last two print commands to print 311 and 311. Instead, they print 311 and 622. I guess that was easy enough to see in the code what is wrong. How can i fix that? how can counter reset each time a command is completed and not when the function is run.
Instead of using global variable you could make the counter a parameter with default value:
def collatz(n, counter=0):
counter += 1
if n <= 0 :
return "Invalid Number"
elif n == 1 :
return counter
elif n % 2 == 1 :
n = 3*n + 1
return collatz(n, counter)
elif n % 2 == 0 :
n = n/2
return collatz(n, counter)
You're using recursion, so just use it properly:
def collatz(n, counter=0):
counter += 1
if n <= 0 :
return "Invalid Number"
elif n == 1 :
return counter
elif n % 2 == 1 :
n = 3*n + 1
return collatz(n, counter)
elif n % 2 == 0 :
n = n/2
return collatz(n, counter)
print(collatz(9921615699))
print(collatz(9921615699))
You were using global in your original version which is usually not what you want to do. You got to see first-hand why.
You could have reset the counter, e.g.
result = counter
counter = 0
return result
But that's pretty nasty, let's not do that. When you're implementing a recursive algorithm there's probably no good reason to have a global variable.

How to count cycles and print it

I have a programm, which uses binary search.
And in the end, i need to print a count of loops.
How it would be better to do?
import re
def binarySearch(sumList, whattofind):
a=0
if len(sumList) == 0:
return False
else:
midpoint = len(sumList)/2
if sumList[midpoint]==whattofind:
a=a+1
print(a)
return True
else:
if whattofind<sumList[midpoint]:
a+=1
return binarySearch(sumList[:midpoint],whattofind)
else:
a+=1
return binarySearch(sumList[midpoint+1:],whattofind)
print(a)
result = re.findall(r'\w\w', open("text.txt","r").read())
sumList=[]
for line in result:
sumList.append(ord(line[0])+ord(line[1]))
sumList.sort()
whattofind=int(input('Enter number: '))
print (sumList)
print(binarySearch(sumList, whattofind))
do the following
count = 0
def binarySearch(sumList, whattofind):
global count
count += 1
and at the last line of code just print value of count

How Nested For loop works

def SNP(seq1, seq2):
result = []
counter = 0
for position, base in enumerate(seq1):
for position2, base2 in enumerate(seq2):
if base != base2:
result.append(position)
result.append(base)
result.append(base2)
counter += 1
if counter == 2:
return None
result2 = tuple(result)
return result2
print(SNP('AAGCCTA', 'AAGCTTA'))
If the "if statement" is invalid, the loop starts again with the 2nd for loop which I did not intend to...
So the question is after the if statement, how should I let the code start again with the first loop instead of 2nd for loop after 1 loop?
def SNP(seq1, seq2):
result = []
counter = 0
for position, base in enumerate(seq1):
for position2, base2 in enumerate(seq2):
if base != base2:
result.append(position)
result.append(base)
result.append(base2)
counter += 1
if counter == 2:
break
result2 = tuple(result)
return result2
Added the break to the if. Sorry if that's the wrong if statement.
You simply neead a break in the if block, like this
if base != base2:
result.append(position)
result.append(base)
result.append(base2)
counter += 1
if counter == 2:
return None
else:
break

Closures in Python - an example [duplicate]

This question already has answers here:
Read/Write Python Closures
(8 answers)
Closed 9 years ago.
I'm performing an action many times in a loop and want to know how far along I am. I'm trying to make a progress report function that should act something like this:
def make_progress_report(n):
i = 0
def progress_report():
i = i + 1
if i % n == 0:
print i
return progress_report
pr = make_progress_report(2)
pr()
pr() # 2
pr()
pr() # 4
This code does not work. Specifically, I get an UnboundLocalError for i. How should I modify it so that it works?
Here are 3 options:
use a list for your counter:
def make_progress_report(n):
i = [0]
def progress_report():
i[0] = i[0] + 1
if i[0] % n == 0:
print i[0]
return progress_report
use itertools.count to track your counter:
from itertools import count
def make_progress_report(n):
i = count(1)
def progress_report():
cur = i.next()
if cur % n == 0:
print cur
return progress_report
Use nonlocal for your counter (Python 3+ only!):
def make_progress_report(n):
i = 0
def progress_report():
nonlocal i
i = i + 1
if i % n == 0:
print i
return progress_report
You could consider using a generator:
def progress_report(n):
i = 0
while 1:
i = i+1
if i % n == 0:
print i
yield # continue from here next time
pr = progress_report(2)
next(pr)
next(pr)
next(pr)
next(pr)
So the progress_report isn't closed over the variable i. You can check it like so...
>>> def make_progress_report(n):
... i=0
... def progress_report():
... i += 1
... if i % n == 0:
... print i
... return progress_report
...
>>> pr = make_progress_report(2)
>>> pr.__closure__
(<cell at 0x1004a5be8: int object at 0x100311ae0>,)
>>> pr.__closure__[0].cell_contents
2
You'll notice there is only one item in the closure of pr. That is the value you passed in originally for n. The i value isn't part of the closure, so outside of the function definition, i is no longer in scope.
Here is a nice discussion on closures in Python: http://www.shutupandship.com/2012/01/python-closures-explained.html
Look again at how you're defining your closure. The n should be passed in when you define the closure... take the following example:
#!/usr/env python
def progressReportGenerator(n):
def returnProgress(x):
if x%n == 0:
print "progress: %i" % x
return returnProgress
complete = False
i = 0 # counter
n = 2 # we want a progress report every 2 steps
getProgress = progressReportGenerator(n)
while not complete:
i+=1 # increment step counter
# your task code goes here..
getProgress(i) # how are we going?
if i == 20: # stop at some arbtirary point...
complete = True
print "task completed"

Categories

Resources