Closures in Python - an example [duplicate] - python

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"

Related

Could anybody tell me why is it returning None value at end other than the required numbers? [duplicate]

This question already has answers here:
Why is "None" printed after my function's output?
(7 answers)
Closed 1 year ago.
def collatz(a):
if a==1:
return a
elif a%2==0:
print(a//2)
collatz(a//2)
else:
print(a*3+1)
collatz(a*3+1)
a = int(input("Enter no. "))
print(collatz(a))
First see Function returns None without return statement
Then your method has nothing to return, because the purpose is to see the path of values and the stop value is always 1
def collatz(a):
print(a)
if a == 1:
return # just the stop case
elif a % 2 == 0:
collatz(a // 2)
else:
collatz(a * 3 + 1)
So call it without a print, you don't expect anything to be returned
a = int(input("Enter value:"))
collatz(a)
You could even combine in
def collatz(a):
print(a)
if a % 2 == 0:
collatz(a // 2)
elif a > 1:
collatz(a * 3 + 1)
You can return the functions directly:
def collatz(a):
if int(a)==1:
return a
elif a%2==0:
print(a//2)
return collatz(a//2)
else:
print(a*3+1)
return collatz(a*3+1)

Python else block doesn't print anything

Here in this code else block is not printing the value Treasure locked
def counted(value):
if(value == 5):
return 1
else:
return 0
def numb(value1):
sam = 95
value = 0
stp = 97
h = {}
for i in range(0,26):
h.update({chr(stp) : (ord(chr(stp))-sam)})
sam = sam-1
stp = stp+1
for j in range(0,5):
value = h[value1[j]]+value
if(value > 80):
print('First lock-unlocked')
else:
print('Treasure locked')
string = input()
firstcheck = counted(len(string))
if(firstcheck == 1):
numb(string)
a good idea is to check what the condition is before entering the if statements, possibly check what value is printing before the if statement. the logic in def numb() has very little do with what's in def counted(). as long as one is 1 or 0 is being passed to numb() we know that function will run and seems like it.
else block is working properly. if you want to print Treasure Locked. you have to pass lower character string like 'aaaaa'. if value is > 80. then it always print First lock-unlocked.

if statement wont take variable 'a' and 'b' when declared in python if statement

I have been curious about how to simplify my work. But for now, my
problem is how to pass variables through functions and to get this If
statement to work. The variable a and b need to pass into the if
statement to check if the string is in the array 'colors' or
'other_colors'
import random;
hot_spot=0;
colors = ['R','G','B','O','P']
other_colors =['RED','GREEN','BLUE','ORANGE','PURPLE']
guesser_array=[]
def code_maker():
code_maker_array=[]
for i in range(4):
ran = random.randint(0,4)
print (ran)
code_maker_array.append(colors[ran])
print(code_maker_array)
return code_maker_array
x = code_maker()
def code_breaker():
trys = 0;
cbi = input('please put in r,g,b,o,p or red,green,blue,orange,purple_ ')
cbi = cbi.upper()
if ( isinstance(cbi,str) == True):
print ('it is a string')
print (cbi)
for i in range(4):
if (len(cbi)>=3):
a = other_colors[i].find(cbi)
else:
b = colors[i].find(cbi)
if (a >= 0 or b >= 0):
print ('yummmeiabui aebfiahfu dsdsde')
y = code_breaker()
"""
def code_checker(x):
print (x)
code_checker(x)
"""
Try this:
import random
hot_spot=0
colors = ['R','G','B','O','P']
other_colors =['RED','GREEN','BLUE','ORANGE','PURPLE']
guesser_array=[]
def code_maker():
code_maker_array=[]
for i in range(4):
ran = random.randint(0,4)
print (ran)
code_maker_array.append(colors[ran])
print(code_maker_array)
return code_maker_array
x = code_maker()
def code_breaker():
trys = 0;
cbi = input('please put in r,g,b,o,p or red,green,blue,orange,purple_ ')
cbi = cbi.upper()
if ( isinstance(cbi,str) == True):
print ('it is a string')
print (cbi)
for i in range(4):
a=b=0 #This line added
if (len(cbi)>=3):
a = other_colors[i].find(cbi)
else:
b = colors[i].find(cbi)
if (a >= 0 or b >= 0):
print ('yummmeiabui aebfiahfu dsdsde')
y = code_breaker()
"""
def code_checker(x):
print (x)
code_checker(x)
"""
The variables a and b you have defined run out of scope as soon as their respective if blocks end. To prevent this, you can simply define them by initializing them to 0 (or any other value) outside of the if statement.
While Lucefer's answer simplified code a lot, I added this because defining variables in an outer scope like this is and modifying their values later on (in the if blocks in your case) is a very common practice, you might find it helpful somewhere else as well.
remove this whole code segment
for i in range(4):
if (len(cbi)>=3):
a = other_colors[i].find(cbi)
else:
b = colors[i].find(cbi)
if (a >= 0 or b >= 0):
print ('yummmeiabui aebfiahfu dsdsde')
just simply add
if( (cbi in other_colors) or (cbi in colors) ):
print ('yummmeiabui aebfiahfu dsdsde')

Python splitting code into functions [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Parameter vs Argument Python [duplicate]
(4 answers)
Closed 5 years ago.
Sorry about the length of this but I figured more info is better than not enough!!
I'm trying to split the (working) piece of Python code into functions to make it clearer / easier to use but am coming unstuck as soon as i move stuff into functions. It's basically a password generator which tries to only output a password to the user once the password qualifies as having a character from all 4 categories in it. (Lowercase, uppercase, numbers and symbols).
import random
import string
lowerasciis = string.ascii_letters[0:26]
upperasciis = string.ascii_letters[26:]
numberedstrings = str(1234567809)
symbols = "!#$%^&*()[]"
password_length = int(raw_input("Please enter a password length: "))
while True:
lowerasscii_score = 0
upperascii_score = 0
numberedstring_score = 0
symbol_score = 0
password_as_list = []
while len(password_as_list) < password_length:
char = random.choice(lowerasciis+upperasciis+numberedstrings+symbols)
password_as_list.append(char)
for x in password_as_list:
if x in lowerasciis:
lowerasscii_score +=1
elif x in upperasciis:
upperascii_score +=1
elif x in numberedstrings:
numberedstring_score +=1
elif x in symbols:
symbol_score +=1
# a check for the screen. Each cycle of the loop should display a new score:
print lowerasscii_score, upperascii_score, numberedstring_score, symbol_score
if lowerasscii_score >= 1 and upperascii_score >= 1 and numberedstring_score >= 1 and symbol_score >=1:
password = "".join(password_as_list)
print password
break
And here is my attempt at splitting it. When i try to run the below it complains of "UnboundLocalError: local variable 'upperascii_score' referenced before assignment" in the scorepassword_as_a_list() function
import random
import string
lowerasciis = string.ascii_letters[0:26]
upperasciis = string.ascii_letters[26:]
numberedstrings = str(1234567809)
symbols = "!#$%^&*()[]"
password_length = int(raw_input("Please enter a password length: "))
lowerasscii_score = 0
upperascii_score = 0
numberedstring_score = 0
symbol_score = 0
password_as_list = []
def genpassword_as_a_list():
while len(password_as_list) < password_length:
char = random.choice(lowerasciis+upperasciis+numberedstrings+symbols)
password_as_list.append(char)
def scorepassword_as_a_list():
for x in password_as_list:
if x in lowerasciis:
lowerasscii_score +=1
elif x in upperasciis:
upperascii_score +=1
elif x in numberedstrings:
numberedstring_score +=1
elif x in symbols:
symbol_score +=1
# give user feedback about password's score in 4 categories
print lowerasscii_score, upperascii_score, numberedstring_score, symbol_score
def checkscore():
if lowerasscii_score >= 1 and upperascii_score >= 1 and numberedstring_score >= 1 and symbol_score >=1:
return 1
else:
return 0
def join_and_printpassword():
password = "".join(password_as_list)
print password
while True:
genpassword_as_a_list()
scorepassword_as_a_list()
if checkscore() == 1:
join_and_printpassword()
break
The primary issue here is that you need to keep track of the scope of the various variables that you're using. In general, one of the advantages of splitting your code into functions (if done properly) is that you can reuse code without worrying about whether any initial states have been modified somewhere else. To be concrete, in your particular example, even if you got things working right (using global variables), every time you called one of your functions, you'd have to worry that e.g. lowerassci_score was not getting reset to 0.
Instead, you should accept anything that your function needs to run as parameters and output some return value, without manipulating global variables. In general, this idea is known as "avoiding side-effects." Here is your example re-written with this in mind:
import random
import string
lowerasciis = string.ascii_letters[0:26]
upperasciis = string.ascii_letters[26:]
numberedstrings = str(1234567809)
symbols = "!#$%^&*()[]"
def genpassword_as_a_list(password_length):
password_as_list = []
while len(password_as_list) < password_length:
char = random.choice(lowerasciis+upperasciis+numberedstrings+symbols)
password_as_list.append(char)
return password_as_list
def scorepassword_as_a_list(password_as_list):
lowerasscii_score = 0
upperascii_score = 0
numberedstring_score = 0
symbol_score = 0
for x in password_as_list:
if x in lowerasciis:
lowerasscii_score +=1
elif x in upperasciis:
upperascii_score +=1
elif x in numberedstrings:
numberedstring_score +=1
elif x in symbols:
symbol_score +=1
# give user feedback about password's score in 4 categories
return (
lowerasscii_score, upperascii_score, numberedstring_score,
symbol_score
)
def checkscore(
lowerasscii_score, upperascii_score, numberedstring_score,
symbol_score):
if lowerasscii_score >= 1 and upperascii_score >= 1 and numberedstring_score >= 1 and symbol_score >=1:
return 1
else:
return 0
def join_and_printpassword(password_as_list):
password = "".join(password_as_list)
print password
password_length = int(raw_input("Please enter a password length: "))
while True:
password_list = genpassword_as_a_list(password_length)
current_score = scorepassword_as_a_list(password_list)
if checkscore(*current_score) == 1:
join_and_printpassword(password_list)
break
A few notes on this:
Notice that the "score" variables are introduced inside the scorepassword_as_list function and (based on the scoping rules) are local to that function. We get them out of the function by passing them out as a return value.
I've used just a bit of magic near the end with *current_score. Here, the asterisk is used as the "splat" or "unpack" operator. I could just as easily have written checkscore(current_score[0], current_score[1], current_score[2], current_score[3]); they mean the same thing.
It would probably be useful to read up a bit more on variable scoping and namespaces in Python. Here's one guide, but there may be better ones out there.

Is this python code thread-safe?

I am trying to make my chunk of code non-thread-safe, in order to toy with some exceptions that I want to add on later.
This is my python code:
from time import sleep
from decimal import *
from threading import Lock
import random
def inc_gen(c):
"""
Increment generator
"""
while True:
#getting sleep period
timing_rand = random.randrange(0,1000)
print "INC: Sleeping for " + str(Decimal(timing_rand)/Decimal(1000))
sleep(Decimal(timing_rand)/Decimal(1000))
c.inc()
yield c
def dec_gen(c):
"""
decrement generator
"""
while True:
#getting sleep period
timing_rand = random.randrange(0,1000)
print "DEC: Sleeping for " + str(Decimal(timing_rand)/Decimal(1000))
sleep(Decimal(timing_rand)/Decimal(1000))
c.dec()
yield c
class something():
"""
We use an obj instead of an atomic variable c, we can have "threads"
simulating shared resources, instead of a single variable, to avoid
atomic instructions. (which is thread-safe in python thanks to GIL)
"""
def __init__(self):
self.c = 0
def inc(self):
self.c += 1
def dec(self):
self.c -= 1
def value(self):
return self.c
def main():
"""
main() function
"""
obj = something()
counters = [inc_gen(obj),dec_gen(obj)]
#we only want inc_gen 10 times, and dec_gen 10 times.
inc = 0 #number of times inc_gen is added
dec = 0 #number of times dec_gen is added
while True:
#choosing the next counter
if inc < 10 and dec < 10:
counter_rand = random.randrange(0,2)
if counter_rand == 0:
inc += 1
else: dec += 1
elif inc < 10 and dec == 10:
inc += 1
counter_rand = 0
elif dec < 10 and inc == 10:
dec += 1
counter_rand = 1
else: break
counters[counter_rand].next()
#print for testing
print "Final value of c: " + str(obj.value())
if __name__ == "__main__":
main()
What I want it to do is to have the code possibly result in a final value which is not 0.
Is it thread-safe? If not, how can I make it such that it is not thread-safe?
You have a Read-Modify-Write operation basically. If you want to ensure things go haywire, the best is to intruduce delay between the read and the write.
def inc(self):
v = self.c
time.sleep(random.random()) # Should probably limit it to a few hundred ms
self.c = v + 1
def dec(self):
v = self.c
time.sleep(random.random()) # Should probably limit it to a few hundred ms
self.c = v - 1

Categories

Resources