about nested function unboundlocalerror in python3 - python-3.x

here is my function with 3 arguments of which two are with default values, and there is a nested function within this function.
def announce_highest(who=1, previous_high=0, previous_score=0):
assert who == 0 or who == 1, 'The who argument should indicate a player.'
# BEGIN PROBLEM 7
"*** YOUR CODE HERE ***"
sentence = "That's the biggest gain yet for Player"
def say_highest(score0, score1):
#nonlocal previous_score, previous_high
print(who, previous_score)
if who == 0:
target = score0
else:
target = score1
score_gap = target - previous_score
if score_gap > previous_high:
print(score_gap, "point" + 's' *(1 - (1 // score_gap)) + '!', sentence, who)
previous_high = score_gap
previous_score = target
return announce_highest(who)
return say_highest
f0 = announce_highest() # Only announce Player 1 score gains
f1 = f0(11, 0)
When i do the assignment to f0, it runs ok. But when doing the assignment to f1, it raised a unboundlocal error:local variable 'previous_score' referenced before assignment.
Why I do something to the argument 'who'(such as print it), it runs normally, but to the argument 'previous_high' and 'previous_score' with same operation, unboundlocal error……
why does this happen? Did not 'who' and 'previous_score' have the same scope?

I got the answer from the professor John. This error is caused by the following assignment statement 'previous_score = target'. Once an assignment of "previous_score" exsits somewhere within the function defined in another function, the previous "previous_score" in the parent frame is no longer accessible.
That's why it raised an error.

Related

using while loop based on argument

Define a function positive that takes an argument (either integer or float with or without a sign) and writes all the positive powers of 2 less than or equal to a valid argument. You must begin writing the function with the following segment of code below and must include a while loop. Hints: consider isdigit() function and continue and break statements.
def positive(n):
i = 0
poweroftwo = 0
Your program should write a message (e.g., Pass a valid argument) if an argument is invalid). Do not use the input function to make this program interactive. Test the function by calling it as follows:
positive(10)
positive(+10)
positive(-10)
positive(10.4)
positive(+10.4)
positive(-10.4)
The output form the 6 calls to the function must be:
1
4
9
1
4
9
Pass a valid arg
Pass a valid arg
Pass a valid arg
Pass a valid arg
The output implies that arguments other than 10 and +10 are all invalid
My code is below this, I don't know if I am doing it right
def isdigit(n):
if n < 1:
print("Pass a valid argument")
return False
elif n != int(n):
print("Pass a valid argument")
return False
else:
return True
def positive(n):
i = 1
poweroftwo = 2
value = isdigit(n)
if value:
while (i ** poweroftwo) < n:
print(i **poweroftwo)
i += 1
positive(10)
positive(+10)
positive(-10)
positive(10.4)
positive(+10.4)
positive(-10.4)

calling functions in python 3 from within a function

Given a string, return the count of the number of times that a substring length 2 appears in the string and also as the last 2 chars of the string, so "hixxxhi" yields 1 (we won't count the end substring).
last2('hixxhi') → 1
last2('xaxxaxaxx') → 1
last2('axxxaaxx') → 2
I found this question in one of the websites (https://codingbat.com/prob/p145834).
The answer to the above question as given on the website is as follows :
def last2(str):
# Screen out too-short string case.
if len(str) < 2:
return 0
# last 2 chars, can be written as str[-2:]
last2 = str[len(str)-2:]
count = 0
# Check each substring length 2 starting at i
for i in range(len(str)-2):
sub = str[i:i+2]
if sub == last2:
count = count + 1
return count
I have a doubt on the below mentioned line of code
last2 = str[len(str)-2:]
Now, I know that this piece of code is extracting the last 2 letters of the string 'str'. What I am confused about is the variable name. As you can see that the variable name is same as the name of the function. So is this line calling the function again and updating the value of the variable 'str' ??
def last2(str):
. . .
This creates a parameter called str that shadows the built-in str class*. Within this function, str refers to the str parameter, not the str built-in class.
This is poor practice though. Don't name your variables the same thing as existing builtins. This causes confusing situations like this, and leads to issues like this.
A better name would be something that describes what purpose the string has, instead of just a generic, non-meaningful str.
* The built-in str is actually a class, not a plain function. str(x) is a call to the constructor of the str class.
def last2(str):
if len(str) == 0:
return 0
last_two = str[-2::]
count = 0
for i in range(len(str)):
if last_two == str[i :i + 2]:
count += 1
return count-1
this is the answer that was correct for me for the first time. The official answer is better, but this one might be less confusing for you.

string indices must be integers error from a for loop but I'm passing an integer

I'm having trouble understanding this error from my code "TypeError: string indices must be integers" due to the fact I believe that I'm already passing the integer to the code:
def col1_button_click(self, x, y):
p = self.players_lst[self.currnt_player_index]
for x in range(6,0,-1):
if self.buttons_2d_list[x][0]["text"] == self.__space:
button = self.buttons_2d_list[x][0]
button["text"] = p.get_player_symbol()
self.gboard.make_move(x, 0, p.get_player_symbol())
winner = self.gboard.check_winner() # The board will check after each move, if any player has won the game
is_full = self.gboard.is_board_full()
if winner == True:
# Show current player's symbol as Winner,
# and terminate the game
win_messge = ("Player %s is the Winner!" % p.get_player_symbol())
messagebox.showinfo("Winner Info ",win_messge)
self.mw.destroy()
exit()
if is_full == True:
messagebox.showinfo("Winner Info", "The game ended in a draw!")
self.mw.destroy()
exit()
else:
pass
if self.currnt_player_index == 1:
self.currnt_player_index = 0
else:
self.currnt_player_index+=1 # increment index by 1
p = self.players_lst[self.currnt_player_index]
p.play()
The error is from this line, 4th one down:
if self.buttons_2d_list[x][0]["text"] == self.__space:
but from my understanding, I'm already passing integers from the range above the line instead of X, if anyone could describe where I've gone wrong, I would be very grateful :)
self.buttons_2d_list[x][0] must be a string...
print it before and see whats inside
...
print(self.buttons_2d_list[x][0])
if self.buttons_2d_list[x][0]["text"] == self.__space:
...
if it returns a string then you are doing:
"string"['text']
which is wrong ...
If
self.buttons_2d_list[x][0]
is a dictionary, you can access the value of the key "text" by writing
self.buttons_2d_list[x][0]["text"]
but if it is anything else you have to access its elements by an integer index like
self.buttons_2d_list[x][0][42]

Evaluating a mathematical expression without eval() on Python3 [duplicate]

This question already has answers here:
Evaluating a mathematical expression in a string
(14 answers)
Closed 10 months ago.
I'm working on a "copy-paste calculator" that detects any mathematical expressions copied to the system clipboard, evaluates them and copies the answer to the clipboard ready to be pasted. However, while the code uses the eval()-function, I'm not terribly concerned considering the user normally knows what they are copying. That being said, I want to find a better way without giving the calculations a handicap (= eg. removing the ability to calculate multiplications or exponents).
Here's the important parts of my code:
#! python3
import pyperclip, time
parsedict = {"×": "*",
"÷": "/",
"^": "**"} # Get rid of anything that cannot be evaluated
def stringparse(string): # Remove whitespace and replace unevaluateable objects
a = string
a = a.replace(" ", "")
for i in a:
if i in parsedict.keys():
a = a.replace(i, parsedict[i])
print(a)
return a
def calculate(string):
parsed = stringparse(string)
ans = eval(parsed) # EVIL!!!
print(ans)
pyperclip.copy(str(ans))
def validcheck(string): # Check if the copied item is a math expression
proof = 0
for i in mathproof:
if i in string:
proof += 1
elif "http" in string: #TODO: Create a better way of passing non-math copies
proof = 0
break
if proof != 0:
calculate(string)
def init(): # Ensure previous copies have no effect
current = pyperclip.paste()
new = current
main(current, new)
def main(current, new):
while True:
new = pyperclip.paste()
if new != current:
validcheck(new)
current = new
pass
else:
time.sleep(1.0)
pass
if __name__ == "__main__":
init()
Q: What should I use instead of eval() to calculate the answer?
You should use ast.parse:
import ast
try:
tree = ast.parse(expression, mode='eval')
except SyntaxError:
return # not a Python expression
if not all(isinstance(node, (ast.Expression,
ast.UnaryOp, ast.unaryop,
ast.BinOp, ast.operator,
ast.Num)) for node in ast.walk(tree)):
return # not a mathematical expression (numbers and operators)
result = eval(compile(tree, filename='', mode='eval'))
Note that for simplicity this allows all the unary operators (+, -, ~, not) as well as the arithmetic and bitwise binary operators (+, -, *, /, %, // **, <<, >>, &, |, ^) but not the logical or comparison operators. If should be straightforward to refine or expand the allowed operators.
without using eval, you'd have to implement a parser, or use existing packages like simpleeval (I'm not the author, and there are others, but I have tested that one successfully)
In one line, plus import:
>>> from simpleeval import simpleeval
>>> simpleeval.simple_eval("(45 + -45) + 34")
34
>>> simpleeval.simple_eval("(45 - 22*2) + 34**2")
1157
now if I try to hack the calculator by trying to import a module:
>>> simpleeval.simple_eval("import os")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "K:\CODE\COTS\python\simpleeval\simpleeval.py", line 466, in simple_eval
return s.eval(expr)
File "K:\CODE\COTS\python\simpleeval\simpleeval.py", line 274, in eval
return self._eval(ast.parse(expr.strip()).body[0].value)
AttributeError: 'Import' object has no attribute 'value'
Caught! the cryptic error message comes from the fact that simpleeval can evaluate variables that you can optionally pass through a dictionary. Catch AttributeError exception to intercept wrongly formed expressions. No need for eval for that.
By native Python3: without using inbuilt function
input_string = '1+1-1*4+1'
result = 0
counter = -1
for ch in range(len(input_string)):
if counter == ch:
continue
if input_string[ch] in ['-', '+', '/', '*', '**']:
next_value = int(input_string[ch+1])
if input_string[ch] == '-':
result -= next_value
counter = ch+1
elif input_string[ch] == '+':
result += next_value
counter = ch+1
elif input_string[ch] == '*':
result *= next_value
counter = ch+1
elif input_string[ch] == '/':
result /= next_value
counter = ch+1
elif input_string[ch] == '**':
result **= next_value
counter = ch+1
else:
result = int(input_string[ch])
print(result)
Output : 
The original string is : '1+1-1*4+1'
The evaluated result is : 5

My python code give this type error ''IndentationError: expected an indented block".give any solution

#!/bin/python
for i in range(1000): #153
pre = i #pre=153
a =str (i) #a='153'
sum=0
for j in a:
k = int (j) #k=1
q = k*k*k
summ = summ+q
if (pre = summ):
print("Armstorng ",pre)
else
print("not Armstorng ",pre)
if (pre = summ):
That should be a double equals sign because it's a comparison between two values, not a variable assignment.
if (pre == summ):
Also, a couple of (potential) spelling errors: "Armstorng" and "summ". (Unless you intend "sum"/"summ" to be different variables.)

Resources