I'm new to Python so I'm sorry if I've made any silly mistake. I have a class with a method isSorted(). I want this method to return True if two lists (a and b) are the same:
class Puzzle:
def isSorted(self):
return set(self.a) == set(self.b)
I use this method outside the class, like this:
puzzle = Puzzle()
while puzzle.isSorted():
#do things
The loop never ends. Is there any way I could solve this, and is it bad code? I've tried
puzzleIsSorted = puzzle.isSorted()
outside the class and re-assigning it in the while loop, but it still didn't work.
edit: full code:
import random
class Puzzle:
def __init__(self):
self.f = open("file.txt", "r")
self.a = self.f.readlines()
self.b = self.a
random.shuffle(self.b)
def swapLine(self, line):
self.b[len(self.b) - 1], self.b[line] = self.b[line], self.b[len(self.b) - 1]
def isSorted(self):
return set(self.a) == set(self.b)
puzzle = Puzzle()
moveCount = 0
print `puzzle.b`
while not puzzle.isSorted():
x = input("Enter a line to swap: ")
puzzle.swapLine(x)
print "\n" + `puzzle.b`
print "Game over in " + `moveCount` + " moves"
First of all, you must copy the list to make a new one in Puzzle's __init__:
def __init__(self):
self.f = open("file.txt", "r")
self.a = self.f.readlines()
# you must copy the list to make another list
self.b = self.a[:]
random.shuffle(self.b)
In your method:
def isSorted(self):
return set(self.a) == set(self.b)
The condition is always True since you only swap lines, you do not add or delete them. sets do not care for order.
If you want to check for order you can drop the set:
def isSorted(self):
return self.a == self.b
You were very close. Make two changes. 1) Don't use sets, stick with lists because they make sure the order matters whereas sets ignore ordering. 2) Be sure to make a copy of the list using slicing rather than an assignment to b. The current code makes a and b aliases for the same list.
Here's some working code to get you started:
import random
text = '''\
Dog
Fox
Cat
Rat
Jay
'''
class Puzzle:
def __init__(self):
self.a = text.splitlines()
self.b = self.a[:] # <== Fixed: Make a copy using slicing
random.shuffle(self.b)
def swapLine(self, line):
self.b[len(self.b) - 1], self.b[line] = self.b[line], self.b[len(self.b) - 1]
def isSorted(self):
return self.a == self.b # <== Fixed: Don't use sets
puzzle = Puzzle()
moveCount = 0
print `puzzle.b`
while not puzzle.isSorted():
x = random.randrange(5) # <== Made this automatic for testing purposes
puzzle.swapLine(x)
print `puzzle.b`
moveCount += 1 # <== Fixed: Update the move counter
print "Game over in " + `moveCount` + " moves"
Related
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():
I know that I have a misunderstanding of how Python attributes work because I'm here writing this problem, but I don't know exactly what I'm misunderstanding. I'm trying to get
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
to attain their values based on self.hand, which I pass to __init__ upon instantiation. Throughout the game I am altering .hand, and I want .card & .card_val to change every time I change .hand, instead of having to tell it to do that elsewhere (outside of the attribute definitions). I know there is a way to do this, or at least I think there is, and by that I mean simply by defining their values as inherited based on whatever .hand is at any given time, without calling an internal or external function.
In the posted code, I have altered it to work as the game instructions require, using...
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
...but that's what I want to change. I want what that function is doing to be executed more concisely inside of the attribute definitions upon handling of the instance. Basically my question is why can't these two attributes get their values directly from the first attribute that I define via "hand" passed to the init?
Any help would be appreciated, and more importantly, I think more than just solutions, it would help me even more to understand what I am misunderstanding about how attributes, instances, and instantiation and all that works so that I know where my thinking is wrong. Thanks!
import random
from random import shuffle
from collections import deque
class Deck():
def __init__(self):
self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
self.suites = ["Heart", "Diamond", "Spade", "Club"]
self.cards = []
def create_cards(self):
for suite in self.suites:
for key in self.ranks.keys():
self.cards.append(key + " " + suite)
def shuffle(self):
random.shuffle(deck.cards)
deck = Deck()
deck.create_cards()
deck.shuffle()
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
def war(bool, p1, p2):
if bool == True:
for player in [p1, p2]:
player.card_number = 4
else:
for player in [p1, p2]:
player.card_number = 0
p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))
p2.name = "The Computer"
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
def cant_war_lose(winner, loser):
print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))
def battle_win(winner, loser):
print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))
def play_cards(p1, p2):
war(False, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-1)
elif p1.card_val == p2.card_val:
if len(p1.hand) < 5 or len(p2.hand) < 5:
if len(p1.hand) > len(p2.hand):
cant_war_lose(p1.name, p2.name)
else:
cant_war_lose(p2.name, p1.name)
return 0
else:
input("War is inititated! Press Enter to continue!")
print("---------------------------")
war(True, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
for i in range(0,5):
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-5)
elif p1.card_val < p2.card_val:
for i in range(0,5):
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-5)
else:
p1.hand.rotate(-1)
p2.hand.rotate(-1)
elif p1.card_val < p2.card_val:
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-1)
if len(p1.hand) != 0 and len(p2.hand) != 0:
input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
print("---------------------------")
else:
if len(p1.hand) > len(p2.hand):
battle_win(p1.name, p2.name)
else:
battle_win(p2.name, p1.name)
return 0
def game_run():
run = 1
p1.name = input("Player 1's name? ")
print("---------------------------")
while run == 1:
if play_cards(p1, p2) == 0:
run = 0
game_run()
You can use the property decorator to create a calculated property
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
#property
def hand(self):
return self._hand
#hand.setter
def hand(self, value):
self._hand = value
self.card = self._hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
What you misunderstand is variables, not instances. For instance, the attribute card is a scalar variable attached to the instance. Assigning to it with
self.card = <blah>
does not bind it to blah for constant recomputation. This is a value assignment, not a memory mapping. If you want that long-term binding, you must either write the maintenance routine yourself -- which you've already done, in a way, with the consistent recomputation -- or you must assign a mutable reference to self.card, so that card refers to teh same object as the expression you created.
Given that you are consistently rotating and altering the hand, this is not feasible in your design. Instead, simply write an access routine, perhaps get_next_card(hand), which will rotate the hand, extract the card, and return the desired rank and suit.
If you plan to program more card games, you will also find it handy to define a class card and class hand, with appropriate support routines. Maintain the card as a pair of integers; convert to strings only for printing.
Does that get you moving?
For anyone who wanted to compare a before and after of the problem & final solution, below is the working code for my specific issue. All I had to do was convert self.card and self.card_val to a calculated property. By passing in hand, and subsequently handling only hand, self.card & self.card_val are calculated, since every time I handle the instance of the class (by handling hand), these "method attributes" are being called and altered. Thanks for the input, guys!
class Player():
def __init__(self, hand):
self.name = "name"
self.card_number = 1
self.hand = hand
#property
def card(self):
return self.hand[self.card_number].split()
#property
def card_val(self):
return deck.ranks.get(self.card[0])
I was kinda playing around with Object Oriented Programming in python and ran into an error i havent encountered before..:
class Main:
def __init__(self, a , b):
self.a = a
self.b = b
def even(self):
start = self.a
slut = self.b
while start <= slut:
if start % 2 == 0:
yield start
start += 1
def odd(self):
start = self.a
slut = self.b
while start <= slut:
if start % 2 != 0:
yield start
start += 1
def display():
evens = list(num.even())
odds = list(num.odd())
print(f"{evens}'\n'{odds}")
num = Main(20, 50)
Main.display()
Take a look at the last class method, where there shouldent be a 'self' as a parameter for the program to Work..Why is that? I thought every class method should include a 'self' as a parameter? The program wont work with it
There should be a self parameter, if it's intended to be an instance method, and you would get an error if you tried to use it as so, i.e., num.display().
However, you are calling it via the class, and Main.display simply returns the function itself, not an instance of method, so it works as-is.
Given that you use a specific instance of Main (namely, num) in the body, you should replace that with self:
def display(self):
evens = list(self.even())
odds = list(self.odd())
print(f"{evens}'\n'{odds}")
and invoke it with
num.display()
I'm trying to have a static int inside a class in python. But it doesn't work.
Here's an example of what I've implemented :
class MyDict(dict):
STR_DEPTH = -1
def __init__(self, **kwargs):
super(MyDict, self).__init__(**kwargs)
self.__dict__.update(name = kwargs.get("name", ""))
def __str__(self):
self.STR_DEPTH += 1
res = self.name + '\n'
for k in self.keys():
res += '\t'*self.STR_DEPTH + k + " = " + str(self[k])
res += '\n'
self.STR_DEPTH -= 1
return res
def main():
d1 = MyDict(one=MyDict())
d1["two"] = 2
d1["one"]["one"] = 1
d1["one"]["two"] = MyDict(three=3)
d1["four"] = 4
print d1
if __name__ == "__main__":
main()
and i'm expecting :
four = 4
two = 2
one =
two =
three = 3
one = 1
but it doesn't work that way. If i'm not mistaking, int aren't references and it's not the same "STR_DEPTH" in every instances of my class.
I already know the list-of-length-1 trick and the empty-type trick, but do i really need to resort do clumsy unreadable trick ?
Isn't there a better way since i'm inside a class ?
Where you have:
self.STR_DEPTH += 1
replace that with:
MyDict.STR_DEPTH += 1
and the same where you decrement the value.
Assigning to self.STR_DEPTH will create a new instance variable which hides access to the class variable through self. You can use self.STR_DEPTH to access the class variable provided you don't have an instance variable of the same name, but if you want to rebind the class variable you have to refer to it directly.
Note that self.STR_DEPTH += 1 is really just shorthand for self.STR_DEPTH = self.STR_DEPTH + 1 so even if the right hand self.STR_DEPTH picks up the class variable the assignment still happens back to the instance variable.
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.