Why can I not alter the value of shape_amount? - python

I am struggling to find a solution to add 1 to the value of shape_amount within a different class and return the value back to compare it.
This is the first class that compares the value of shape_amount.
class BEGIN_DRAWING:
def begin_draw(run, shape_opt, color_value, color):
global shape_amount
shape_amount = 0
i = True
while i == True:
color = color_value(color)
run.movement(shape_amount, color)
print(shape_amount)
if shape_amount == shape_opt:
i = False
run.movement(shape_amount, color)
I print shape_amount to check its values but outputs 0
class DRAWING_CURVED:
def fill(shape_amount, color):
rand_int = randint(0, 20)
if rand_int == 0:
end_fill()
fillcolor(color)
begin_fill()
shape_amount += 1
return shape_amount
This is supposed to add 1 to the value of shape_amount every time a shape is filled but it does not seem to be doing so.

From what I can tell, you are passing the shape_amount global variable into a function as a parameter. This parameter will not be treated as the global variable, but as a normal parameter.
The below highlights this, it prints for me:
2
1
2
def test():
global x
x = 1
val = increment(x)
print(val)
print(x)
increment_two()
print(x)
def increment(x):
return x + 1
def increment_two():
global x
x += 1
if __name__ == "__main__":
test()

Related

How do I accomplish the same thing without using a global variable?

add_digits2(1)(3)(5)(6)(0) should add up all the numbers and stop when it reaches 0.
The output should be 15
The below code works but uses a global variable.
total = 0
def add_digits2(num):
global total
if num == 0:
print(total)
else:
total += num
return add_digits2
The result is correct but needs to do the same thing without using the global variable.
One thing you could do is use partial:
from functools import partial
def add_digits2(num, total=0):
if num == 0:
print(total)
return
else:
total += num
return partial(add_digits2, total=total)
add_digits2(2)(4)(0)
You can just pass in *args as a parameter and return the sum
def add_digits2(*args):
return sum(args)
add_digits2(1, 3, 5 ,6)
You could also use a class, using the __call__ method to obtain this behavior:
class Add_digits:
def __init__(self):
self.total = 0
def __call__(self, val):
if val != 0:
self.total += val
return self
else:
print(self.total)
self.total = 0
add_digits = Add_digits()
add_digits(4)(4)(0)
# 8
add_digits(4)(6)(0)
# 10
though I still don't get why you would want to do this...
Really hard to say what they are after when asking questions like that but the total could be stored in a function attribute. Something like this
>>> def f():
... f.a = 3
>>> f()
>>> f.a
3

How can I keep numbers in memory in a calculator app?

I am making a calculator with a GUI but I don't know how to keep track of a number so that I can use it later on with a second number to perform an operation.
My code is as follows:
def Sum():
Plus = pygame.image.load(os.path.abspath("plus.png"))
screen.blit(Plus,(555,34))
c=a+b
return c
def Sustraction():
Minus = pygame.image.load(os.path.abspath("minus.png"))
screen.blit(Minus,(555,34))
c=a-b
return c
def Multiplication():
Times = pygame.image.load(os.path.abspath("Times.png"))
screen.blit(Times,(555,34))
c=a*b
return c
def Division():
Division = pygame.image.load(os.path.abspath("Division.png"))
screen.blit(Division,(555,34))
c=a/b
return c
def button (msg, x, y, w, h, ic, ac, action=None ):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if (x+w > mouse[0] > x) and (y+h > mouse[1] > y):
pygame.draw.rect(CALCULATOR, WHITE, (x, y, w, h))
if (click[0] == 1 and action != None):
if (action == "Sum"):
Sum()
elif (action == "Substraction"):
Substraction()
elif (action == "Multiplication"):
Multiplication()
elif (action == "Division"):
Division()
if (action == "One"):
One()
a=1
return a
elif (action == "Two"):
Two()
a=2
return a
elif (action == "Three"):
Three()
a=3
return a
So as you can see in my functions definition, when I call Sum I use c=a+b. The problem is I don't know how to keep a in memory to use it for the sum operation with b, because of this I don't know how to input a new value and save it in b.
How can I store the value of a while having the user input a new value into b?
First of all, you should try to use class.
If its about your question. Simply use functions parameters.
Example with sum function:
def Sum(a, b):
Plus = pygame.image.load(os.path.abspath("plus.png"))
screen.blit(Plus,(555,34))
c=a+b
return c
If you want to do it in "real time" you can use only 1 parameter and keep result as class member.
def Sum(a):
Plus = pygame.image.load(os.path.abspath("plus.png"))
screen.blit(Plus,(555,34))
self.result += a
If it does not suffice read about Reverse Polish notation
In Python, variables that are only referenced inside a function are
implicitly global. If a variable is assigned a value anywhere within
the function’s body, it’s assumed to be a local unless explicitly
declared as global.
- What are the rules for local and global variables in Python?
Therefore when you assign a variable a and c in the function, it is created as a local variable. If you would try to use it before assignment, as example:
def increment():
c += 1
return c
you would get UnboundLocalError: local variable 'c' referenced before assignment exception.
I think in your example you would also get a NameError: name 'a' is not defined exception if you would call Sum() before button() callback is executed, as a is not yet defined. That is because a is referenced in the function, but not assigned - therefore is not created as a local variable, however it is also not to be found anywhere in global namespace.
The easiest solution would be to use global variables. Not that whenever you want to use global variable instead of creating a local one, you need to show it in the local context with the global keyword:
a = 0
b = 0
c = 0
def sum():
global a, b
Plus = pygame.image.load(os.path.abspath("plus.png"))
screen.blit(Plus,(555,34))
c = a + b
return c
def button(msg, x, y, w, h, ic, ac, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
global a
if ....
However I would suggest using class and store values as a properties, as example:
class Calculator:
def __init__(self) -> None:
self.x = 0
self.result = 0
self.screen = ... # Your initialization
self.actions = {
"Sum": self.sum,
"Substraction": self.subtract,
"Multiplication": self.multiply,
"Division": self.divide
}
self.numbers = {
"One": 1,
"Two": 2,
"Three": 3
}
def sum(self, x):
plus = pygame.image.load(os.path.abspath("plus.png"))
self.screen.blit(plus, (555, 34))
self.result += x
def subtract(self, x):
minus = pygame.image.load(os.path.abspath("minus.png"))
self.screen.blit(minus, (555, 34))
self.result -= x
def multiply(self, x):
times = pygame.image.load(os.path.abspath("Times.png"))
self.screen.blit(times, (555, 34))
self.result *= x
def divide(self, x):
division = pygame.image.load(os.path.abspath("Times.png"))
self.screen.blit(division, (555, 34))
if x != 0:
self.result /= x
else:
print("I refuse to divide by 0.")
def button(self, msg, x, y, w, h, ic, ac, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if ((x + w) > mouse[0] > x) and ((y + h) > mouse[1] > y):
pygame.draw.rect(CALCULATOR, WHITE, (x, y, w, h))
if (click[0] == 1) and (action is not None):
if action in self.actions:
self.actions[action]()
elif action in self.numbers:
self.x = self.numbers[action]
In this example I also used in keyword and dictionary of values, which simplify selection instead of using bunch of if-elif cases.

Python best practices for functions

Approach 1 (global var):
id_constant = 1000
id_cnt = 1
def give_id():
global id_cnt
id_cnt += 1
return id_constant * id_cnt
id = give_id()
Approach 2 (fuc var instead of global var):
id_cnt = 1
def give_id():
id_constant = 1000
global id_cnt
id_cnt += 1
return id_constant * id_cnt
id = give_id()
Approach 3 (pass in global vars):
id_cnt = 1
id_constant = 1000
def give_id(constant, cnt):
return constant * cnt
global id_cnt
id_cnt +=1
id = give_id(id_constant, id_cnt)
im not sure if there are any general rule of thumb but is is widely accepted for a function to access a global variable inside a function? or if the variable is only used for a function, then should it be part of a function variable instead?
The method often depends a little on the situation.
You seem to need unique ids, why not use a generator:
def create_id_generator():
"""Returns an id generator."""
i = 0
while True:
yield i
i += 1
Used with the next() function:
>>> ID_GENERATOR = create_id_generator() # Global variable
>>> my_id = next(ID_GENERATOR)
>>> my_id2 = next(ID_GENERATOR)
>>> my_id3 = next(ID_GENERATOR)
>>> print(my_id, my_id2, my_id3, next(ID_GENERATOR))
0 1 2 3
If you want the ids to be multiples of 1000, you can pass the constant to the generator via parameters:
def create_id_generator(multiplier=1000):
"""Returns an id generator."""
i = 0
while True:
yield i * multiplier
i += 1
You can even add a starting value if you don't want to start from index 0:
def create_id_generator(multiplier=1000, start_index=0):
"""Returns an id generator."""
while True:
yield start_index * multiplier
start_index += 1
If id_constant is actually constant, I would have done:
ID_CONSTANT = 1000
def give_id(id_count):
return ID_CONSTANT * id_count
id_count = 1
id = give_id(id_count)
But it looks like you also have some state (id_count) that needs to be kept up-to-date with the issuing of id, suggesting a generator function:
def give_id(id_count):
while True:
yield ID_CONSTANT * id_count
id_count += 1
or even a class:
class IdCreator(object):
ID_CONSTANT = 1000
def __init__(self, start_count=1):
self.id_count = start_count
def give_id(self):
new_id = self.ID_CONSTANT * self.id_count
self.id_count += 1
return new_id
You could go further and implement iteration for the class.
Global variable is generally something you should avoid.
If you want to have constants, for let's say, configuration purposes I would take more a module approach like:
conf.py
MYCONST = 1000
app.py
import conf
print conf.MYCONST
Or take an OO approach such as:
class Test(object):
def __init__(self):
self._constant = 1000
def give_id(self, cnt):
return self._constant * cnt
From the Zen of Python (i.e. import this)
Namespaces are one honking great idea -- let's do more of those!
In general, if you don't need to put something in the global namespace, it is better to encapsulate it in the local namespace of the function, so I would consider option 2 to be more "pythonic" unless id_constant is going to be used by multiple functions.
You might also try the following using a keyword argument with a default value:
id_cnt = 1
def give_id(id_constant=1000):
global id_cnt
id_cnt += 1
return id_constant * id_cnt
id = give_id()
Then if you ever needed id_constant to be something different, you could call the function as id = give_id(id_constant=500).
A little bit of tricky stuff:
def get_id_func(constant):
class c(object):
def __init__(self, constant):
self.constant = constant
self.id = 0
def func(self):
self.id += 1
return self.id * self.constant
o = c(constant)
return o.func
# create function
f = get_id_func(1000)
# call and test it
assert f() == 1000
assert f() == 2000
assert f() == 3000
Probably you need generator function?
def give_id(id_constant):
delta = 0
while True:
delta += 1
yield id_constant + delta
for i in range(100):
print(give_id(1000)) # prints numbers from 1001 to 1100

Calling a method from the same class

I'm writing a class for a simple game of 4 in a row, but I'm running into a problem calling a method in the same class. Here's the whole class for the sake of completeness:
class Grid:
grid = None
# creates a new empty 10 x 10 grid
def reset():
Grid.grid = [[0] * 10 for i in range(10)]
# places an X or O
def place(player,x,y):
Grid.grid[x][y] = player
# returns the element in the grid
def getAt(x,y):
return Grid.grid[x][y]
# checks for wins in a certain direction
def checkLine(player,v,count,x,y):
x = x+v[0]
y = y+v[1]
if x < 0 or x > 9:
return
if y < 0 or y > 9:
return
if Grid.grid[x][y] == p:
count = count+1
if count == 4:
return True
checkLine(player,v,count,x,y)
return False
# returns the number of the player that won
def check():
i = 'i'
for x in range(0,10):
for y in range(0,10):
if Grid.grid[x][y] > 0:
p = Grid.grid[x][y]
f = checkLine(p,0,array(i,[1,0]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[0,1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[1,1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[-1,0]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[0,-1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[-1,-1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[1,-1]),x,y)
if f:
return p
f = checkLine(p,0,array(i,[-1,1]),x,y)
if f:
return p
return 0
reset = staticmethod(reset)
place = staticmethod(place)
getAt = staticmethod(getAt)
check = staticmethod(check)
checkLine = staticmethod(checkLine)
I'm trying to call checkLine() from check(), but I get the error "NameError: global name 'checkLine' is not defined". When I call Grid.checkLine() instead, I get "TypeError: 'module' object is not callable"
How do I call checkLine()?
EDIT:
#beer_monk
class Grid(object):
grid = None
# creates a new empty 10 x 10 grid
def reset(self):
Grid.grid = [[0] * 10 for i in range(10)]
# places an X or O
def place(self,player,x,y):
Grid.grid[x][y] = player
# returns the element in the grid
def getAt(self,x,y):
return Grid.grid[x][y]
# checks for wins in a certain direction
def checkLine(self,player,v,count,x,y):
x = x+v[0]
y = y+v[1]
if x < 0 or x > 9:
return
if y < 0 or y > 9:
return
if Grid.grid[x][y] == p:
count = count+1
if count == 4:
return True
checkLine(self,player,v,count,x,y)
return False
# returns the number of the player that won
def check(self):
i = 'i'
for x in range(0,10):
for y in range(0,10):
if Grid.grid[x][y] > 0:
p = Grid.grid[x][y]
for vx in range(-1,2):
for vy in range(-1,2):
f = self.checkLine(p,0,array(i,[vx,vy]),x,y)
if f:
return p
return 0
reset = staticmethod(reset)
place = staticmethod(place)
getAt = staticmethod(getAt)
check = staticmethod(check)
checkLine = staticmethod(checkLine)
Get rid of the class. Use plain functions and module level variable for grid.
The class is not helping you in any way.
PS. If you really want to call checkline from within the class, you'd call Grid.checkline. For example:
class Foo:
#staticmethod
def test():
print('Hi')
#staticmethod
def test2():
Foo.test()
Foo.test2()
prints
Hi
Syntax:
class_Name.function_Name(self)
Example:
Turn.checkHoriz(self)
A reworked example (hopefully showing a better use of classes!)
import itertools
try:
rng = xrange # Python 2.x
except NameError:
rng = range # Python 3.x
class Turn(object):
def __init__(self, players):
self.players = itertools.cycle(players)
self.next()
def __call__(self):
return self.now
def next(self):
self.now = self.players.next()
class Grid(object):
EMPTY = ' '
WIDTH = 10
HEIGHT = 10
WINLENGTH = 4
def __init__(self, debug=False):
self.debug = debug
self.grid = [Grid.EMPTY*Grid.WIDTH for i in rng(Grid.HEIGHT)]
self.player = Turn(['X','O'])
def set(self, x, y):
if self.grid[y][x]==Grid.EMPTY:
t = self.grid[y]
self.grid[y] = t[:x] + self.player() + t[x+1:]
self.player.next()
else:
raise ValueError('({0},{1}) is already taken'.format(x,y))
def get(self, x, y):
return self.grid[y][x]
def __str__(self):
corner = '+'
hor = '='
ver = '|'
res = [corner + hor*Grid.WIDTH + corner]
for row in self.grid[::-1]:
res.append(ver + row + ver)
res.append(corner + hor*Grid.WIDTH + corner)
return '\n'.join(res)
def _check(self, s):
if self.debug: print("Check '{0}'".format(s))
# Exercise left to you!
# See if a winning string exists in s
# If so, return winning player char; else False
return False
def _checkVert(self):
if self.debug: print("Check verticals")
for x in rng(Grid.WIDTH):
winner = self._check([self.get(x,y) for y in rng(Grid.HEIGHT)])
if winner:
return winner
return False
def _checkHoriz(self):
if self.debug: print("Check horizontals")
for y in rng(Grid.HEIGHT):
winner = self._check([self.get(x,y) for x in rng(Grid.WIDTH)])
if winner:
return winner
return False
def _checkUpdiag(self):
if self.debug: print("Check up-diagonals")
for y in rng(Grid.HEIGHT-Grid.WINLENGTH+1):
winner = self._check([self.get(d,y+d) for d in rng(min(Grid.HEIGHT-y, Grid.WIDTH))])
if winner:
return winner
for x in rng(1, Grid.WIDTH-Grid.WINLENGTH+1):
winner = self._check([self.get(x+d,d) for d in rng(min(Grid.WIDTH-x, Grid.HEIGHT))])
if winner:
return winner
return False
def _checkDowndiag(self):
if self.debug: print("Check down-diagonals")
for y in rng(Grid.WINLENGTH-1, Grid.HEIGHT):
winner = self._check([self.get(d,y-d) for d in rng(min(y+1, Grid.WIDTH))])
if winner:
return winner
for x in rng(1, Grid.WIDTH-Grid.WINLENGTH+1):
winner = self._check([self.get(x+d,d) for d in rng(min(Grid.WIDTH-x, Grid.HEIGHT))])
if winner:
return winner
return False
def isWin(self):
"Return winning player or False"
return self._checkVert() or self._checkHoriz() or self._checkUpdiag() or self._checkDowndiag()
def test():
g = Grid()
for o in rng(Grid.WIDTH-1):
g.set(0,o)
g.set(Grid.WIDTH-1-o,0)
g.set(Grid.WIDTH-1,Grid.HEIGHT-1-o)
g.set(o,Grid.HEIGHT-1)
print(g)
return g
g = test()
print g.isWin()
Unlike java or c++, in python all class methods must accept the class instance as the first variable. In pretty much every single python code ive seen, the object is referred to as self. For example:
def reset(self):
self.grid = [[0] * 10 for i in range(10)]
See http://docs.python.org/tutorial/classes.html
Note that in other languages, the translation is made automatically
There are multiple problems in your class definition. You have not defined array which you are using in your code. Also in the checkLine call you are sending a int, and in its definition you are trying to subscript it. Leaving those aside, I hope you realize that you are using staticmethods for all your class methods here. In that case, whenever you are caling your methods within your class, you still need to call them via your class's class object. So, within your class, when you are calling checkLine, call it is as Grid.checkLine That should resolve your NameError problem.
Also, it looks like there is some problem with your module imports. You might have imported a Module by name Grid and you have having a class called Grid here too. That Python is thinking that you are calling your imported modules Grid method,which is not callable. (I think,there is not a full-picture available here to see why the TypeError is resulting)
The best way to resolve the problem, use Classes as they are best used, namely create objects and call methods on those objects. Also use proper namespaces. And for all these you may start with some good introductory material, like Python tutorial.
Instead of operating on an object, you are actually modifying the class itself. Python lets you do that, but it's not really what classes are for. So you run into a couple problems
-You will never be able to make multiple Grids this way
the Grid can't refer back to itself and e.g. call checkLine
After your grid definition, try instantiating your grid and calling methods on it like this
aGrid = Grid()
...
aGrid.checkLine()
To do that you, you first need to modify all of the method definitions to take "self" as your first variable and in check, call self.checkLine()
def check(self):
...
self.checkLine()
...
Also, your repeated checking cries out for a FOR loop. You don't need to write out the cases.
Java programmer as well here, here is how I got it to call an internal method:
class Foo:
variable = 0
def test(self):
self.variable = 'Hi'
print(self.variable)
def test2(self):
Foo.test(self)
tmp = Foo()
tmp.test2()

Python __init__ issue: unbound method __init__() must be called with Bank instance as first argument (got int instance instead)

class Teller(object):
def __init__(self):
self.occupied = False
self.timeLeft = 0
self.totTime
def occupy(self, timeOcc):
self.occupied = True
self.timeLeft = timeOcc
def nextMin(self):
self.timeLeft -= 1
self.totTime += 1
if self.timeLeft == 0:
self.occupied = False
class Bank(object):
def __init__(numTellers, hoursOpen):
self.tellers = []
self.timeWaited = 0
self.clientsWaiting = []
for x in xrange(numTellers):
tempTeller = Teller.__init__()
self.tellers.append(tempTeller)
self.minutesOpen = hoursOpen * 60
def tellerOpen(self):
for x in xrange(len(self.tellers)):
if not self.tellers[x].occupied:
return x+1
return 0
def runSim(self, queueInput): #queueInput is a list of tuples (time, timeAtTeller)
simTime = self.minutesOpen
totCli = 0
timeToNext = queueInput[0][0]
timeAtNext = queueInput[0][1]
queueInput.pop(0)
self.clientsWaiting.append([timeToNext, timeAtNext])
while simTime > 0:
for person in self.clientsWaiting:
if person[0]:
person -= 1
if not self.clientsWaiting[len(self.clientsWaiting)-1][0]:
timeToNext = queueInput[0][0]
timeAtNext = queueInput[0][1]
queueInput.pop(0)
self.clientsWaiting.append([timeToNext, timeAtNext])
remove = 0
for x in xrange (len(self.clientsWaiting)-1):
if tellerOpen() and not self.clientsWaiting[x][0]:
self.tellers[tellerOpen()].occupy(self.clientsWaiting[x][0])
totCli += 1
remove += 1
elif not tellerOpen() and not self.clientsWaiting[x][0]:
self.timeWaited += 1
for x in xrange(remove):
self.clientsWaiting.pop(x)
print """The total time spent in the queue by all clients was %d minutes. The total number of clients today was %d. The average waiting time was %d mins""" % (self.timeWaited, totCli, self.timeWaited / totCli)\
if __name__ == '__main__':
inp = raw_input()
tList = inp.split('\n')
qList = []
for item in tList:
tList = item.split(' ')
qList.append((tList[0], tList[1]))
virtBank = Bank.__init__(3, 7)
bank.runSim(qList)
This results in this error:
> TypeError: unbound method __init__() must be called with Bank instance as first argument (got int instance instead)
I don't see what I've dont wrong. Any advice would be appreciated.
The only important parts, I think, are the Bank class __init__ and the call virtBank = Bank.__init__(3, 7)
2 points to make here:
You shouldn't be calling __init__ directly, it's a magic method which is invoked when you construct an object like this:
virtBank = Bank(3, 7)
The instance is implicitly passed to the constructor, but it must be explicitly received, like this:
def __init__(self, numTellers, hoursOpen):
# ...

Categories