When I attempted to write a recursive nested function within a python class, every time the recursive function completed and returned back to the first function my class property reverted back to the original state.
def main():
input = [
[1,3,1],
[1,5,1],
[4,2,1]
]
sln = Solution()
sln.findPath(input)
print("Output: " + str(sln.minPathSum))
print(*sln.minPath, sep = "->")
class Solution():
minPath = []
minPathSum = None
grid = []
def findPath(self, grid):
self.minPath = []
self.minPathSum = None
self.grid = grid
self.recurse(0,0,[])
def recurse(self, xCoord, yCoord, currentPath):
if(len(self.grid) <= yCoord):
return
if(len(self.grid[yCoord]) <= xCoord):
return
currentValue = self.grid[yCoord][xCoord]
currentPath.append(currentValue)
if(len(self.grid) - 1 == yCoord and len(self.grid[yCoord]) - 1 == xCoord):
currentPathSum = sum(currentPath)
if(self.minPathSum == None or currentPathSum < self.minPathSum):
self.minPathSum = currentPathSum
self.minPath = currentPath
else:
#right
self.recurse(xCoord + 1, yCoord, currentPath)
#down
self.recurse(xCoord, yCoord + 1, currentPath)
currentPath.pop()
return
if __name__ == "__main__":
main()
Running this results in:
Output: 7
Debugging the code within VSCode does indicate that self.minPath is getting set within the recurse function; however, it appears to be losing scope to the original class instance.
In addition I attempted to recreate the same nested situation with separate code and ended up with the following.
def main():
tst = ScopeTest()
tst.run()
print(*tst.tstArray)
print(tst.tstNumber)
class ScopeTest():
tstArray = []
tstNumber = 0
def run(self):
self.otherFunc()
def otherFunc(self):
self.tstArray = [1,2,3,4,5]
self.tstNumber = 7
if __name__ == "__main__":
main()
The above did return the expected outcome which leads me to think this has something to do with the recursion.
Just a heads up, I'm fairly new to python so I may be making a rookie mistake, but I can't seem to figure out why this is happening.
Your recurse() method is generating a currentPath parameter and when this is deemed to be correct you execute: self.minPath = currentPath. Unfortunately, you just reference the same object as currentPath which is later mutated.
Your line should be: self.minPath = currentPath[:]
You then see some contents in self.minPath
Mandatory link to Ned Batchelder
Also you can remove these lines:
minPath = []
minPathSum = None
grid = []
from just below class Solution():
Related
I'm trying to get around this error but can't find a solution. Actually the code worked perfectly some time but suddendly started to drop the error:
"NameError: name 'number_exit' is not defined"
And also:
"NameError: name 'price_buy' is not defined"
The code is generating a list of random numbers
import numpy as np
# This function generates a list of numbers under certain rules
def num_var_list():
global number_list, number_exit, number_target
number_list = []
number_target = number_exit
num_max_robot = (number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1,int(num_max_robot))
if num_robot > number_target:
number_list.append(number_target)
else:
number_list.append(num_robot)
number_target = number_target - number_target
return number_list
# This function generates a random number between a certain range
def fun_price_buy():
global price_buy
price_buy = np.random.randint(50000,300000)
return price_buy
# This function generates a random number between a certain range
def fun_mx_buy():
global number_exit
number_exit = np.random.randint(50, 150)
return number_exit
lista_number_list = []
lista_price_buy = []
lista_mx_buy = []
# This loop append each function 50 times to a new list
while len(lista_price_buy) <= 50:
lista_number_list.append(num_var_list())
lista_price_buy.append(fun_price_buy())
lista_mx_buy.append(fun_mx_buy())
Actually, when Python doesn't drop the error, the code makes exactly what I want it to do. So I'm not sure how to adjust it to let it work without NameError warnings.
Any help will be highly appreciated. Thank you!
When doing global price_buy that means you use the globally defined price_buy to be defined localy in your method but
neither price_buy nor number_exit are defined globally (outside a method)
you don't need global variable
They only to be local, and just better : inlined
def fun_price_buy():
price_buy = np.random.randint(50000,300000)
return price_buy
# inline, no temporaty variable is needed
def fun_price_buy():
return np.random.randint(50000,300000)
Finally, and if you want to get the value from the method in a variable for doing something with it:
import numpy as np
# This function generates a list of numbers under certain rules
def num_var_list(number_exit):
number_list = []
number_target = number_exit
num_max_robot = (number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1,int(num_max_robot))
if num_robot > number_target:
number_list.append(number_target)
else:
number_list.append(num_robot)
number_target = number_target - number_target
return number_list
def fun_price_buy():
return np.random.randint(50000,300000)
def fun_mx_buy():
return np.random.randint(50, 150)
lista_number_list = []
lista_price_buy = []
lista_mx_buy = []
# This loop append each function 50 times to a new list
while len(lista_price_buy) <= 50:
number_exit = fun_mx_buy()
price_buy = fun_price_buy()
vr_list = num_var_list(number_exit)
lista_number_list.append(vr_list)
lista_price_buy.append(price_buy )
lista_mx_buy.append(number_exit )
why do you have so much global ?
def num_var_list():
number_exit=fun_mx_buy()
number_list = []
number_target = number_exit
num_max_robot = (number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1, int(num_max_robot))
if num_robot > number_target:
number_list.append(number_target)
else:
number_list.append(num_robot)
number_target = number_target - number_target
return number_list
is it works for you?
It looks like you are defining variables via globals inside a function. You'll want to convert your price_buy and number_exit to local variables.
or:
# This function generates a random number between a certain range
def fun_price_buy():
return np.random.randint(50000,300000)
# This function generates a random number between a certain range
def fun_mx_buy():
return np.random.randint(50, 150)
If you still want to use global variables (as other answers pointed, not a recommended approach), you will have to give them a value before using them:
number_exit = 0
# This function generates a list of numbers under certain rules
def num_var_list():
global number_list, number_exit, number_target
number_list = []
You can instantiate number_exit in the scope of the function as well, but you need to do so before actually using it.
# This function generates a list of numbers under certain rules
def num_var_list():
global number_list, number_exit, number_target
number_list = []
number_exit = 0
You haven't defined those global variables. Typing global whatever doesn't automatically define a variable called whatever. It just tells the interpreter that there's an existing variable called whatever in global scope.
For example, the following code produces no errors:
blah = 'yada'
def foo(bar):
global blah
print(blah, bar)
foo('test') # output: yada test
Whereas in this example, if the global variable isn't defined beforehand (I commented it out), it gets the same error as you:
#blah = 'yada'
def foo(bar):
global blah
print(blah, bar)
foo('test') # output: NameError: name 'blah' is not defined
So, to stop getting the error, you have to give your global variables some value beforehand, like None. Although you could be avoiding globals altogether if you used a class to hold those values you need, like this:
import numpy as np
class MyClass(object):
def __init__(self):
#you'll have to set the numerical values to something that causes num_var_list to not loop infinitely
self.number_list = None
self.number_exit = 0
self.number_target = 0
self.price_buy = 0
# This function generates a list of numbers under certain rules
def num_var_list(self):
self.number_list = []
self.number_target = self.number_exit
num_max_robot = (self.number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1,int(num_max_robot))
if num_robot > self.number_target:
self.number_list.append(self.number_target)
else:
self.number_list.append(num_robot)
self.number_target = self.number_target - self.number_target
return self.number_list
# This function generates a random number between a certain range
def fun_price_buy(self):
self.price_buy = np.random.randint(50000,300000)
return self.price_buy
# This function generates a random number between a certain range
def fun_mx_buy(self):
self.number_exit = np.random.randint(50, 150)
return self.number_exit
def main():
lista_number_list = []
lista_price_buy = []
lista_mx_buy = []
my_class_instance = MyClass()
# This loop append each function 50 times to a new list
while len(lista_price_buy) <= 50:
lista_number_list.append(my_class_instance.num_var_list())
lista_price_buy.append(my_class_instance.fun_price_buy())
lista_mx_buy.append(my_class_instance.fun_mx_buy())
if __name__ == '__main__':
main()
Apologies for this newbie question. I'm not sure if I even phrased it correctly.
I have a class inside a function that lists a bunch of variables, and I want to be able to choose which variables are printed and returned at the final function call. However, I clearly don't understand enough about objects to accomplish this, because it raises errors when I try to attempt something.
def gpscall(call):
#Write GPSinput
out = ''
ser.write(com.GPSstatus.encode())
time.sleep(1)
#Read output
while ser.inWaiting() > 0:
decoded = (ser.read(1).decode)
out += decoded()
strlen = len(str(out))
substr = out[0:strlen-9]
#GPS? information list
variables = substr.splitlines()
#Storing each output in a variable
class GPS:
PULSE_SAWTOOTH = [int(s) for s in variables[1] if s.isdigit()]
TRACKED_SATELLITES = [int(s) for s in variables[2] if s.isdigit()]
VISIBLE_SATELLITES = [int(s) for s in variables[3] if s.isdigit()]
LONGITUDE = variables[5]
longlen = len(LONGITUDE)
LONGDEG = LONGITUDE[0:longlen-7]
LONGMIN = LONGITUDE[longlen-7:]
LATITUDE = variables[6]
latlen = len(LATITUDE)
LATDEG = LATITUDE[0:latlen-7]
LATMIN = LATITUDE[latlen-7:]
HEIGHT = variables[7]
KNOTS = variables[8]
DEGREES = [9]
GPS_STATUS = variables[10]
TIMING_MODE = variables[17]
FIRMWARE_VERSION = variables[20]
print (call)
return (call)
if __name__ == "__main__":
#Call the functions
gpscall(gpscall.GPS.LATITUDE)
This raises the error,
Function 'gpscall' has no 'GPS' member.
I don't understand why it cannot see the class, I think I'm using the function parameters incorrectly.
Any help with my poorly written code would be greatly appreciated.
Perhaps something like so is your intention? __init__ will initialize the object, and the self. will "save variables to the object."
class GPS:
def __init__(self):
#Write GPSinput
ser.write(com.GPSstatus.encode())
#Read output
out = ''
while ser.inWaiting() > 0:
decoded = (ser.read(1).decode)
out += decoded()
#GPS information list
substr = out[0:len(str(out))-9]
variables = substr.splitlines()
self.PULSE_SAWTOOTH = [int(s) for s in variables[1] if s.isdigit()]
self.TRACKED_SATELLITES = [int(s) for s in variables[2] if s.isdigit()]
self.VISIBLE_SATELLITES = [int(s) for s in variables[3] if s.isdigit()]
self.LONGITUDE = variables[5]
self.LONGDEG = LONGITUDE[0:len(LONGITUDE)-7]
self.LONGMIN = LONGITUDE[len(LONGITUDE)-7:]
self.LATITUDE = variables[6]
self.LATDEG = LATITUDE[0:len(LATITUDE)-7]
self.LATMIN = LATITUDE[len(LATITUDE)-7:]
self.HEIGHT = variables[7]
self.KNOTS = variables[8]
self.DEGREES = variables[9]
self.GPS_STATUS = variables[10]
self.TIMING_MODE = variables[17]
self.FIRMWARE_VERSION = variables[20]
gps = GPS()
print(gps.GPS_STATUS)
Yor cls inside the function is perfect and there is nothing wrong there. You are just trying to call the function and the cls objects in a wrong way.
if __name__ == "__main__":
#Call the functions
gpscall(gpscall.GPS.LATITUDE) <---- ERROR HERE
gpscall is a function, so when you are trying to access GPS.LATITUDE, it won't find any objects. You would have to do either this
gpscall(gpscall("").GPS.LATITUDE)
But I think the best way to do this is to write the func inside the cls. You will still be able to access all the variables of the cls, and it won't create much hassle.
PS: That's a good question, not a noob one. Good luck (y)
I am writing a function to load a previously saved game in chess using Pygame. I have six classes: main2, GUI2, navigation2, calc, board and pieces. I store the attribute that stores where each chess piece is on the board class like so:
class board:
def __init__(self,main):
self.main = main
self.pieces = self.main.pieces
self.prv_pos = []
self.dict = self.pieces.dict
A = ["bRook","bKnight","bBishop","bQueen","bKing","bBishop","bKnight","bRook"]
B = ["bPawn","bPawn","bPawn","bPawn","bPawn","bPawn","bPawn","bPawn"]
C = [0,0,0,0,0,0,0,"wKing"]
D = [0,0,0,0,0,0,0,0]
E = [0,0,0,0,"wQueen",0,0,0]
F = [0,0,0,0,0,0,0,0]
G = ["wPawn","wPawn","wPawn","wPawn","wPawn","wPawn","wPawn","wPawn"]
H = ["wRook","wKnight","wBishop","wQueen",0,"wBishop","wKnight","wRook"]
self.piece_pos= [A,B,C,D,E,F,G,H]
I also have a class called main that is passed an instance on each class possible thus all my objects interact with each other through the main class. Thus in my navigation class that hosts the functions to save and load the game I wrote this:
class navigation:
def __init__(self,GUI):
self.GUI = GUI
self.main = self.GUI.main
self.piece_pos = self.GUI.main.board.piece_pos
self.GUI.draw_button("save",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM)
self.GUI.draw_button("load",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM*3)
self.GUI.draw_button("exit",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM*5)
def game_save(self):
file = open("Save file.txt","w")
for line in self.piece_pos:
for item in line:
file.write(str(item)+",")
file.write("\n")
file.close()
def game_load(self): #WIP
self.piece_pos = []
self.GUI.draw_board()
As you may notice that the game_load function is pretty empty; that is because I wanted to check that in the self.piece_pos = [] line that the attribute is actually cleared throughout the classes. But when I call the self.GUI.draw_board() function which just draws the current board positions which are stored in piece_pos in the class board the board's appearance in the GUI is the same. I expect an error message in the python shell telling me there is no self.piece_pos[i][j] but it seems to me that the attribute hasn't changed whatsoever.
The function for draw_board() is stored in the class 'GUI'.
def draw_board(self):
X = 0
Y = 0
Ycounter = False
sqcounter = False
#Draw the board
print("GUI update")
for i in range(0,8):
for j in range(0,8):
print()
print("Piece at:")
print(i,j)
print(self.main.piece_at(i,j))
pg.draw.rect(self.window, self.sq_colour[i][j],(X,Y,self.SQ_DIM,self.SQ_DIM),0)
if self.main.piece_at(i,j) == 0:
print("square empty")
pass
else:
print("square occupied")
self.window.blit(self.dict[self.main.piece_at(i,j)],(X,Y))
#Change of X coord
if X >(self.SQ_DIM*6):
X = 0
Ycounter = True
sqcounter = True
else:
X +=(self.SQ_DIM)
#Change of Y coord
if Ycounter == True:
Y +=self.SQ_DIM
Ycounter = False
else:
pass
So I have come to the conclusions that I am not understanding something about how to globalize the piece_pos attribute. But I don't know how to solve this conundrum? Any ideas why?
This is because in game_load(), self.piece_pos refers to navigation.piece_pos, while draw_board() uses the object referenced by GUI.main.board.piece_pos.
You're effectively doing this
a = [5]
b = a
print(a, b) # prints [5] [5]
# By reassigning `b` you only change what it refers to,
# it doesn't affect what it used to refer to before.
b = []
print(a, b) # prints [5] []
# Instead, if you make an *in-place* modification, then the underlying
# object will indeed change, as both names refer to the same object.
a = b = [5]
b.append(3)
print(a, b) # prints [5, 3] [5, 3]
If you really want to make a change, it should be
def game_load(self): #WIP
self.main.board.piece_pos = []
self.GUI.draw_board()
Since the navigation class is a wrapper of GUI, it might be best to get rid of self.piece_pos and use function calls to get/set the piece positions, e.g.,
def get_piece_pos(self):
return self.main.board.piece_pos
def set_piece_pos(self, new_pos):
self.main.board.piece_pos = new_pos
Overall though, I don't see the point of this class. The save/load/exit buttons belong to the GUI, as should the respective save/load functions. I would just integrate this functionality in the GUI class, as this is the object the user interacts with.
Tcard.attack(self.players[self.opponent])
AttributeError: 'int' object has no attribute 'attack'
This is the error I get from calling attack().
Tcard = self.players[self.turn].returnCard()
Tcard.attack(self.players[self.opponent])
For some odd reason when Tcard.attack() calls with the parameters self.players[self.opponent], the list returns an int ranther than a Player object. Can someone please explain why it is returning an int rather than an object?
Here is the code for the whole file:
class Game():
def __init__(self):
self.players = []
self.turn = 0
self.opponent = 1
def prepare_game(self):
AI_1 = AI("AI_1", 1000)
AI_1.generate_inventory()
AI_2 = AI("AI_2", 1000)
AI_2.generate_inventory()
self.players.append(AI_1)
self.players.append(AI_2)
def start(self):
p1 = self.players[self.turn]
p2 = self.players[self.opponent]
Tcard = self.players[self.turn].returnCard()
print "Battle time"
print "%s attacked %s" % (p1.Name, p2.Name)
Tcard.attack(self.players[self.opponent])
#switch
if self.turn == 0:
self.turn = 1
self.opponent = 0
self.start()
else:
self.turn = 0
self.opponent = 1
self.start()
Here is where the function returnCard is at:
class AI():
def __init__(self, Name, Health):
self.Name = Name
self.Health = Health
self.inventory = []
def generate_inventory(self):
#Generate 6 Cards and 3 Power-ups
#rand_powerups = random.randint(min(len(powerup)), max(len(powerup)))
rand_cards = random.randint(0, 4)
#self.inventory.append(rand_powerups)
while len(self.inventory) != 4:
self.inventory.append(rand_cards)
if len(self.inventory) == 4:
break
def returnCard(self):
return self.inventory[random.randrange(0, 4)]
def returnCard(self):
return self.inventory[random.randrange(0, 4)]
returnCard returns a random item of self.inventory.
And self.inventory is filled by generate_inventory which does this:
# generate a random *INT*
rand_cards = random.randint(0, 4)
while len(self.inventory) != 4:
# append that *INT*
self.inventory.append(rand_cards)
# (note that this keeps adding the same number over and over)
So, of course, returnCard will return an int here:
Tcard = self.players[self.turn].returnCard()
So when you try to call attack you try to call it on an int:
Tcard.attack(self.players[self.opponent])
self.inventory = [] # a list
rand_cards = random.randint(0, 4)
self.inventory.append(rand_cards) # you add ints to the list
# you return an int
return self.inventory[random.randrange(0, 4)]
# you set Tcard equal to an int returned from ^^
Tcard = self.players[self.turn].returnCard()
On another note you should use range to add the random ints and keep calling randint or you will just get the same number added to your list:
def generate_inventory(self):
for _ in range(4):
self.inventory.append(random.randint(0, 4))
If you want to use the methods in your class, create an instance. I could give you an example but I have no idea where attack comes from.
First of all, Tcard becomes a variable due to this line:
Tcard = self.players[self.turn].returnCard()
By assigning Tcard the result of .returnCard() which will always be an integer since you made returnCard() to return an integer with:
return self.inventory[random.randrange(0, 4)]
So since an integer can't have any attributes, that will be an error. Thus the raised error saying that an int has no attribute.
Second, Tcard is not even a function. Only functions can have attributes thus adding more to the error. You need to create a function for Tcard to be able to work. Add something like:
class Tcard:
def attack():
#Do something
I have a python 2.7 script which is getting pretty unweildly.
The script runs constantly, in each loop it checks a few things, compares them to the previous run and makes some decisions.
The biggest problem I have is with the variables, I have half a dozen and I am limited in how I use them due to scope.
eg;
import time
import os
LOG_FILE = "/var/log/sc.log"
CHECK_FILE_TMP = "/tmp/myfile"
CHECK_FILE_USR = "/home/snoppy/testfile"
CHECK_FILE_TMP_TIME_INTERVAL = 20
CHECK_FILE_USR_TIME_INTERVAL = 30
def main():
try:
last_file_tmp_size = 0
last_file_usr_size = 0
last_file_tmp_mtime = 0
last_file_usr_mtime = 0
last_file_tmp_check_time = 0
last_file_usr_check_time = 0
tmp_file_changed = False
usr_file_changed = False
loop_start_time = 0
print "Starting loop"
while True:
loop_start_time = time.time()
if (time.time() - last_file_tmp_check_time > CHECK_FILE_TMP_TIME_INTERVAL):
tmp_file_changed = checkFileChanged(CHECK_FILE_TMP, last_file_tmp_size, last_file_tmp_mtime)
last_file_tmp_size = getFileSize(CHECK_FILE_TMP)
last_file_tmp_mtime = getFileMTime(CHECK_FILE_TMP)
if(tmp_file_changed):
logChange(CHECK_FILE_TMP, last_file_tmp_size, last_file_tmp_mtime)
last_file_tmp_check_time = time.time()
....
....
sleep(1)
...
So thats sort of what I am dealing with.
I have local variables, which I seem to be stuck with, I have to pass them around into functions - I don't want to call them global.
Ideally.... if I could get a main() function like
try:
checkFile(CHECK_FILE_TMP)
checkFile(CHECK_FILE_USR)
sleep(0.1)
except:
...
as the main is so big! and I have to pass around the variables everywhere... it feels like the main function is so bloated!
Perhaps.... I might have to go for a class?
You need to identify the parts of your program that can be abstracted from their specific details and reused multiple times.
This is an art, don't expect it to be easy. You can find many books on Object oriented programming that explain the basics and give you some direction.
Here's just a quick example:
class FileChecker(object):
def __init__(self, path, interval):
self.path = path
self.interval = interval
self.last_size = None
self.last_mtime = None
self.last_check_time = None
def hasChanged(self):
...
def logChange(self):
...
class MultiFileChecker(object):
DELAY = 1
def __init__(self):
self.files = []
self.loop_start_time = 0
def addFile(self, f):
self.files.append(f)
def loop(self):
try:
print "Starting loop"
while True:
now = time.time()
self.loop_start_time = now
for f in self.files:
if now - f.last_check_time > f.interval:
if f.hasChanged():
f.logChange()
...
sleep(self.DELAY)
...
if __name__ == '__main__':
checker = MultiFileChecker()
checker.add(FileChecker("/var/log/sc.log", 10))
checker.add(FileChecker("/tmp/myfile", 20))
checker.add(FileChecker("/home/snoppy/testfile", 30))
checker.loop()