Not able to declare a global variable in python - python

I was trying to make a quiz with tkinter and python classes(I have never used classes). Does anyone have a explanation to why I am getting this error "PS C:\Users\user_name> & python "c:/python projects/tkinter_w_classes.py"
File "c:/python projects/tkinter_w_classes.py", line 17
global selected = 'a'
^
SyntaxError: invalid syntax"
class Question():
def __init__(self,question, op_a,op_b,op_c,op_d, answer):
self.question = question
self.op_a = op_a
self.op_b = op_b
self.op_c = op_c
self.op_d = op_d
self.answer = answer
def makewin(self):
root = tk.Tk()
root.geometry('500x500')
def bexea():
global selected = 'a'
if selected == answer:
global score += 1
def bexeb():
selected = 'b'
if selected == answer:
score += 1
def bexec():
selected = 'c'
if selected == answer:
score += 1
def bexed():
selected = 'd'
if selected == answer:
score += 1
ql = tk.Label(text=question).pack()
ba = tk.Button(text=op_a,command=bexea).pack()
bb = tk.Button(text=op_b,command=bexeb).pack()
bc = tk.Button(text=op_c,command=bexec).pack()
bd = tk.Button(text=op_d,command=bexed).pack()
q1 = Question()
q1('What is 2 + 2', '1', '2', '3', '4', '4')
q1.makewin()

A global statement cannot perform assignment; it just marks the name as global, rather than local. All global statements must appear at the start of the function.
def bexea():
global selected, score
selected = 'a'
if answer == selected:
score += 1
There is little reason to make selected global, though. No function looks at what value it may have received previously; they all simply set a new value and use that. If you define selected at all, it can simply be a local variable.
def bexea():
global score
selected = 'a'
if selected == answer:
score += 1
There are other issues with your class design (including the use of any global variables), but that's beyond the scope of this question.

class Question():
selectecd = ''
def markSelected(self):
self.selected = 'a'
def printSelected(self):
print(self.selected)
This should be a proper class based approach. Don't use global ... it should be enough to have it in a instance based scope.

Related

Python - Unexpected behaviour of class attributes

Edited in simple words
code:
class temp:
attr1 = 0
attr2 = []
t1 = temp()
t2 = temp()
t1.attr1 = 50
t1.attr2.append(50)
print(t1.attr1)
print(t1.attr2)
print(t2.attr1)
print(t2.attr2)
output:
50
[50]
0
[50]
I have called append only on attr2 object t1 but the append changes attr2 of both objects. if attr2 is shared (class attributes) then why does attr1 values are different for t1 and t2. What might have caused this unexpected behaviour ?
old question
I am writing a python code for blackjack. The code I have written is as follows.
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.cards)
print(p1.cards)
dealer.hit()
print(dealer.cards)
print(p1.cards)
print(dealer.total)
print(p1.total)
Game = False
output of this code is as follows
Welcome to BlackJack ....
Enter the amount you currrently have for the game55
[]
[]
[45]
[45]
6
0
as you can see I had called hit() only once on dealer object but it is appending it to cards attribute of both dealer as well as p1 object. However total attribute is different. Can anyone explain what might have caused this unexpected behaviour ?
When you do t1.attr1 = 50, you're rebinding attr1 to a new value in the t1 object's attribute namespace. It previously let you access the value bound in the class namespace, but when you bind a new value, you hide the one from the class (for that instance only).
In contrast, when you do t1.attr2.append(50), you're mutating the existing list (which is bound in the class namespace, but is visible though all instances) in place, with no rebinding of variables happening at all. This is why you see the change in t2. The variables t1.attr2 and t2.attr2 are both references to the same object (which you can verify using the is operator: t1.attr2 is t2.attr2).
In general, it's usually not a good idea to use lists or other mutable values for class variables if you don't want them to be shared by all instances. It's not forbidden though, because sometimes you do specifically do want the shared behavior.
I got what you are asking. You need to differentiate all cards with player cards. So, instead of naming everything as cards, I would suggest doing this:
class Player:
all_cards = []
total = 0
amount = 0
and update __init__ as :
def __init__(self, money=0):
self.amount = money
self.player_cards = []
while doing append operation, append it to all_cards and to the player_cards. Anyway, you are printing only player cards, you can see different list of cards.
Here is full code :
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
all_cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.player_cards = []
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.player_cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.player_cards.append(no)
self.all_cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.player_cards)
print(p1.player_cards)
dealer.hit()
print(dealer.player_cards)
print(p1.player_cards)
print(dealer.total)
print(p1.total)
Game = False
This happened because list is a mutable object, and it is created once only when defining the class, that is why it becomes shared when you create two instances. Therefore, to solve this problem, we can use constructor like what I have mentioned above. When we put the list in constructor, whenever the object is instantiated, the new list will also be created.

Python - UnboundLocalError: local variable "ID" referenced before assignment

Id, conf = recognizer.predict(gray[y:y+h,x:x+w]
def hour(cn):
for z in range(9,17):
if now.hour == z:
worksheet(cn, str(z)+":00")
def identify(number):
sht = gc.open("Test")
wks3 = sht.worksheet("NAMES")
b = wks3.acell('B'+str(number)).value
a = wks3.acell('A'+str(number)).value
if(Id == a and conf<65):
print(Id, conf)
Id = str(b)
Time = time.ctime()
hour(number)
elif(conf>64):
print(conf)
Id = "Unknown"
for m in range(2,100):
identify(m)
The above code is being used for facial recognition, I copied what I felt was necessary, it is not the entire code.
I'm trying create a function which I want to call back in a for loop
What am I doing wrong? I've been looking t this for 6 hours now, and anything I try doesn't seem to work.
I get a message back saying "UnboundLocalError: local variable 'Id' referenced before assignment"
It's impossible because I'm assigning with:
a = wks3.acell('A'+str(number)).value
So it grabs the ID number from the google spread sheet and checks if it is equaled to that, can someone tell me where I'm going wrong here?
def identify(number):
sht = gc.open("Test")
wks3 = sht.worksheet("NAMES")
b = wks3.acell('B'+str(number)).value
a = wks3.acell('A'+str(number)).value
#because you did, Id = ?
if(Id == a and conf<65):
print(Id, conf)
Id = str(b)
Time = time.ctime()
hour(number)
elif(conf>64):
print(conf)
Id = "Unknown"
Because you did, variable Id isn't passed as any parameter or global/local variable or as an argument to existing class.
If Id was parameter:
def identify(number,Id):
If Id was global variable:
def identify(number):
global Id
If Id was local variable:
def identify(number):
id = None # or some other data type
And if Id was argument from some class:
some_class.Id
In short you referenced Id before it was initialised. This is rookie mistake and there is some stuff where you can actually init a variable in if elif else statement but you need to trow a none of above logic of the rule.
if True: Id = 2; elif False: Id = 3; else: Id =0 #this is pseudocode, don't paste it in.
Also have in mind that next variable is also Unbound conf
EDIT:
Often to avoid this problem we write code like this:
def somefunction(parm1,parm2... ):
# global variables : description for variable stack is optional
global var1,var2 # if needed
#local variables
var3,var4 = None;
var5 = 'something else'
#in body functions : functions inside functions or just general program functions
def a(... ): return ...
#body : actually what function/program does.
# returning , finishing statement.

Python alter external variable from within function

When I run this it works, but it says
"name 'select_place' is assigned to before global declaration"
When I get rid of the second global, no comment appears, but as select_place is no longer global it is not readable (if selected) in my last line of code.
I'm really new to python, ideally I'd like a way of not using the global command but after searching i still can't find anything that helps.
My code:
def attempt(x):
if location =='a':
global select_place
select_place = 0
if location =='b'
global select_place
select_place = 1
place = ([a,b,c,d])
This is the start of some turtle graphics
def Draw_piece_a(Top_right):
goto(place[select_place])
You need to declare the variable first, additionally the function code can be made clearer:
select_place = False
def attempt(x):
global select_place
if location == 'a':
select_place = 0
elif location == 'b':
select_place = 1
Also, there is no return value for attempt(), is this what you want?

Change object's variable from different file

I want to access object (specifically its variables) from functions defined in different file. Let's see an example:
File 1 - grail.py
import enemies
class Encounter:
def __init__(self):
self.counter = 1
self.number = 0
self.who = "We've encountered no one."
def forward(self):
if self.counter == 1:
enemies.knightofni()
elif self.counter == 2:
enemies.frenchman()
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
knight = Encounter()
for i in range(4):
print(str(knight.number) + " " + knight.who)
knight.forward()
File 2 - enemies.py (I probably need something in this file)
def knightofni():
Object.number = 1
Object.who = "We've encountered Knight of Ni."
def frenchman():
Object.number = 4
Object.who = "We've encountered French."
Output should show:
0 We've encountered no one.
1 We've encountered Knight of Ni.
4 We've encountered French.
42 We've found the Grail!
I know you can achieve the output by returning something from functions in file enemies.py, for example function frenchman() could look like:
def frenchman():
return [4, "We've encountered French."]
and in grail.py I should change code to collect what the frenchman() returns:
...
elif self.counter == 2:
spam = enemies.frenchman()
self.number = spam[0]
self.who = spam[1]
...
but it uses additional resources, makes the code longer, and more cumbersome in more complicated situations.
Is there a way to do the job directly on the object's variables but keeping functions in separate file?
EDIT
There are already answers to this question but maybe I will add clarification seeing doubt in one of the answers (citing comment to this answer):
I want it to be possible to add other "enemies" without making lengthy code in this place (so forward() is kind of a wrapper, place where it is decided what to do in different situations). It is also more readable if this functions are in different file.
Think of situation where there would be 100 "enemies" and each would need to change 100 variables which are lists with 1M entries each. Is there a better way than putting "enemies" into other file and changing variables directly in the file?
Problem
You need to hand over the object as argument.
In the function:
def knightofni(obj):
obj.number = 1
obj.who = "We've encountered Knight of Ni."
and when using it in the class:
enemies.knightofni(self)
Do the same for frenchman().
Full code
grail.py
import enemies
class Encounter:
def __init__(self):
self.counter = 1
self.number = 0
self.who = "We've encountered no one."
def forward(self):
if self.counter == 1:
enemies.knightofni(self)
elif self.counter == 2:
enemies.frenchman(self)
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
knight = Encounter()
for i in range(4):
print(str(knight.number) + " " + knight.who)
knight.forward()
and enemies.py:
def knightofni(obj):
obj.number = 1
obj.who = "We've encountered Knight of Ni."
def frenchman(obj):
obj.number = 4
obj.who = "We've encountered French."
Output:
0 We've encountered no one.
1 We've encountered Knight of Ni.
4 We've encountered French.
42 We've found the Grail!
It is possible to do this, though I don't know why you would really want to do it this way.
In your forward and __init__ methods you'll notice that you are passing in self, which is the instance of Encounter you are operating on. That is why you can do self.number = 42 and get the correct number when you call knight.number.
Since self is just an object you can pass it into the functions in 'enemies.py'.
Try:
# grail.py
def forward(self):
if self.counter == 1:
enemies.knightofni(self)
elif self.counter == 2:
enemies.frenchman(self)
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
#enemies.py
def knightofni(that):
that.number = 1
that.who = "We've encountered Knight of Ni."
def frenchman(that):
that.number = 4
that.who = "We've encountered French."

cannot find attribute in imported file

I am trying to make a button interface for my program which is imported as rlg. rlg has a live graphing system in which two variables can be measured and updated as the generations of the simulation go on. I want to be able to make these two variables selective so i made a dictionary 'graphLines' in the main() method of rlg in which each string selection on the drop down menu acts as a key. However i dont seem to be able to access it and get the error message: AttributeError: 'function' object has no attribute 'graphLines'. Can anyone see what i am doing wrong.
from Tkinter import *
import runlivegraph3 as rlg
def run():
rlg.main()
def setLine1(name):
rlg.main.Line1data = rlg.main.graphLines[name] #graphlines is a dictionary in runlivegraph3 main method
def setLine2(name):
rlg.main.Line2data = rlg.main.graphLines[name]
root = Tk()
var1 = StringVar()
var1.set("select graph line 1 data") #initial variable in drop down menu, each string is a key in the graphLines dictionary
op1 = OptionMenu(root, var1, 'Political attacks in turn',
'Ethnic attacks in turn',
'Total attacks in turn',
'Ethnic attacks as a percentage of total attacks',
'Political attacks as a percentage of total attacks',
'Group 1 ethnic antagonism',
'Group 2 ethnic antagonism',
command = setLine1).pack()
var2 = StringVar()
var2.set("select graph line 2 data") #initial variable in drop down menu
op2 = OptionMenu(root, var2, 'Political attacks in turn',
'Ethnic attacks in turn',
'Total attacks in turn',
'Ethnic attacks as a percentage of total attacks',
'Political attacks as a percentage of total attacks',
'Group 1 ethnic antagonism',
'Group 2 ethnic antagonism',
command = setLine2).pack()
butn = Button(root, text = 'run', command = run)
butn.pack()
root.mainloop()
this is the main() function of the program i am importing in the Tkinter button program
from matplotlib.pylab import *
import sys, random, time, csv
def main():
IDs = {}
boardDims = (20,20)
Line1data = None
Line2data = None
turnLimit = 40
pause = 0.0001
ethnicPred = []
politicalPred = []
totalAttacks = []
generation = []
data1 = []
data2 = []
data3 = []
ethAnt1 = []
ethAnt2 = []
polAnt1 = []
polAnt2 = []
EthnicAttacksInTurn = []
PoliticalAttacksInTurn = []
TotalAttacksInTurn = []
ProportionEth = []
ProportionPol = []
board = make_board(boardDims)
finallyAddAgents(IDs, board, boardDims)
splitAgents(IDs)
setRemainingPolitics(IDs)
setPoliticalAntagonism(IDs)
turn = 0
line1, = plot(turn, 0, 'b') #initialise lines
line2, = plot(turn, 0, 'r')
running = 1
while running:
ion() #sets up graph base and axes
axes()
xlim(0,turnLimit)
ylim(0,30)
if turn == turnLimit: running = 0
print_board3(IDs, board, boardDims)
print 'turn ', str(turn)
polAttackTurn = []
ethAttackTurn = []
AllAgentsPerformActions(IDs, board,turn,ethnicPred, politicalPred,
totalAttacks,polAttackTurn,ethAttackTurn)
totalAttackTurn = sum(ethAttackTurn) + sum(polAttackTurn)
if totalAttackTurn != 0:
propEth = (sum(ethAttackTurn)*100)/totalAttackTurn
propPol = (sum(polAttackTurn)*100)/totalAttackTurn
if totalAttackTurn == 0:
propEth = 0
propPol = 0
TotalAttacksInTurn.append(totalAttackTurn)
EthnicAttacksInTurn.append(sum(ethAttackTurn))
PoliticalAttacksInTurn.append(sum(polAttackTurn))
ProportionEth.append(propEth)
ProportionPol.append(propPol)
k = sum(politicalPred)
j = sum(ethnicPred)
#f = sum(totalAttacks)
#print k, j, f
data1.append(j)
data2.append(k)
#data3.append(f)
generation.append(turn)
for agent in IDs.values():
if agent.group == '1':
ethAnt1.append(agent.antagonism['2'])
break
for agent in IDs.values():
if agent.group == '2':
ethAnt2.append(agent.antagonism['1'])
break
for agent in IDs.values():
if agent.politics == 'A':
polAnt1.append(agent.polAntagonism['B'])
break
for agent in IDs.values():
if agent.politics == 'B':
polAnt2.append(agent.polAntagonism['A'])
break
#this is the dictionary i am trying to access from the Tkinter button program
graphLines = {'Political attacks in turn':sum(polAttackTurn),
'Ethnic attacks in turn':sum(ethAttackTurn),
'Total attacks in turn':totalAttackTurn,
'Ethnic attacks as a percentage of total attacks': propEth,
'Political attacks as a percentage of total attacks': propPol,
'Group 1 ethnic antagonism': ethAnt1[-1],
'Group 2 ethnic antagonism': ethAnt2[-1]}
line1.set_ydata(append(line1.get_ydata(), Line1data))
line1.set_xdata(append(line1.get_xdata(), turn))
line2.set_ydata(append(line2.get_ydata(), Line2data))
line2.set_xdata(append(line2.get_xdata(), turn))
draw()
turn += 1
I figured I'd better turn my comment into an answer, so here I go.
You are getting confused between the difference between variables and attributes, so I'll explain the difference with some examples. Your question is not a matter of importing actually, but more about scope and object oriented programming (OOP).
(e.g. 1) To set a local variable within a function, you can do:
def spam():
eggs = 5
(e.g. 2) To set an attribute on a function object (which is usually not so logical), you can do:
def spam():
pass
spam.eggs = 5
While these may appear to be similar, their effect is very different. In the first example, eggs is a local variable within the function spam. Local variables are created, accessed, and modified only within their defining function.
def spam():
eggs = 5
print spam.eggs
will result in an error, however
def spam():
pass
spam.eggs = 5
print spam.eggs
will not. In the second example, eggs is an attribute of the function (object) spam. It can be created, accessed, and modified both within a method of the object or outside the object, but not within the function itself as a local variable (also because the function does not know of its existence until it is fully defined). Therefore, the following would raise an error:
def spam():
print eggs
spam.eggs = 5
spam()
because eggs is an attribute, not a local variable.
If you are familiar with OOP, here is some expansion:
The first example would be equivalent to:
class Spam(object):
def __init__(self):
eggs = 5
while the second example would be equivalent to:
class Spam(object):
def __init__(self):
self.eggs = 5
In terms of OOP, the difference is simply that the first sets a local variable, while the second sets an instance variable. Trying to do Spam().eggs on the first class would not make sense, while on the second it could.
Finally,
To solve your problem, either define the variables you need outside of a function, or use the global keyword to show that they are global. Example usage:
def spam():
global eggs
eggs = 5
spam()
print eggs # Prints 5

Categories