Related
I am creating an object that represents the hand of a blackjack player. One of the methods of the hand is to add a new Card to it. However, my Hand object always returns None when I attempt to print it.
Here is my code of the Hand object.
class Hand:
'''An object representing the Card objects that the player has in their hands'''
def __init__(self, name):
self.name = name
self.list = []
def addCard(self, card):
self.list = self.list.append(card)
return self.list
def __str__(self):
return f'Your hand has {self.list}.'
myHand = Hand('Henry')
myHand.addCard(str(myCard))
print (myHand)
myCard is an object that returns "Four of Diamonds" I created previously. Below is the whole code if you are interested.
class Card:
''' A class for representing a single playing card. '''
def __init__(self, value, suit):
''' Creates Card object with given suit and value. '''
self.value = value
self.suit = suit
def getSuit(self):
''' Returns the suit of the Card. '''
return self.suit
def getValue(self):
''' Returns the value of the Card. '''
return self.value
def getBlackjackValues(self):
''' Get a list of possible Blackjack values for the card. '''
# IMPLEMENT ME
if 1 < self.value:
BlackjackValue = self.value
return [BlackjackValue]
else:
BlackjackValue = [self.value, 11]
return BlackjackValue
def __str__(self):
''' #Return a string representation of the Card. '''
# IMPLEMENT ME
# Convert numerical values into letters
if self.value == 2:
Value = 'Two'
elif self.value == 3:
Value = 'Three'
elif self.value == 4:
Value = 'Four'
elif self.value == 5:
Value = 'Five'
elif self.value == 6:
Value = 'Six'
elif self.value == 7:
Value = 'Seven'
elif self.value == 8:
Value = 'Eight'
elif self.value == 9:
Value = 'Nine'
elif self.value == 10:
Value = 'Ten'
elif self.value == 11:
Value = 'Jack'
elif self.value == 12:
Value = 'Queen'
elif self.value == 13:
Value = 'King'
elif self.value == 1:
Value = 'Ace'
# Convert suit values into letter
if self.suit == 'S':
Suit = 'Spades'
elif self.suit == 'H':
Suit = 'Hearts'
elif self.suit == 'D':
Suit = 'Diamonds'
elif self.suit == 'C':
Suit = 'Clubs'
# The card is
return f'Your card is {Value} of {Suit}.'
myCard = Card (4, 'D')
print (myCard)
class Hand:
'''An object representing the Card objects that the player has in their hands'''
def __init__(self, name):
self.name = name
self.list = []
def getName(self):
return self.name
def getList(self):
return self.list
def addCard(self, card):
self.list = self.list.append(card)
return self.list
def __str__(self):
return f'Your hand has {self.list}.'
myHand = Hand('Henry')
myHand.addCard(str(myCard))
print (myHand)
Here is the screenshot of the output:
Output
list.append() method works in place, i.e. it returns None. That is what you assign to self.list. Note that if you try to add second card it will raise an error, because None has no append attribute.
All I need to do is use .append() at the return statement in the addCard method.
Also, credits to #Nja for pointing out that I do not need to update myHand object again, but simply initiate the method.
Try this:
class Hand:
'''An object representing the Card objects that the player has in their hands'''
def __init__(self, name):
self.name = name
self.list = []
def addCard(self, card):
self.list.append(card)
def __str__(self):
return 'Your hand has ' + ' '.join(self.list)
myCard = "ciao"
myHand = Hand('Henry')
myHand.addCard(str(myCard))
print (myHand)
for java, we can do:
for(int i=100; i>2 ; i=i/2){things to execute}
but what if in python?
is there anything like
for i in range(100:2:something)
could solve this problem?
If you need something simple which you can have at hand at several places, you can create a generator function:
def range_divide(start, end, denominator): # TODO: Think for a better name!
value = start
while value > end:
yield value
value /= denominator
and then do
for value in range_divide(100, 2, 2):
# do_stuff
You could even flexibilize this with
def range_flexible(start, end, action):
value = start
while value > end:
yield value
value = action(value)
and do
for value in range_flexible(100, 2, lambda x: x/2):
# do_stuff
or even
def for_loop(start, cont_condition, action):
value = start
while cont_condition(value):
yield value
value = action(value)
for value in for_loop(100, lambda x: x > 2, lambda x: x/2):
# do_stuff
There isn't by using a range, you could prepopulate a list and iterate over that but you'd be better off using a while loop.
i = 100
while i > 2:
...
i = i / 2
If you want it to look more like a java (or C) for loop, you can define a function that will process the parameters as a string in the C style (at the expense of execution speed):
cachedCFor = dict()
def cFor(params):
if params in cachedCFor: return cachedCFor[params]()
setup,condition,step = [ p.strip() for p in params.split(";") ]
varName = setup.split("=",1)[0].strip()
fn = dict()
code = f"""
def iterator():
{setup}
while {condition}:
yield {varName}
{step}
"""
exec(code,{},fn)
cachedCFor[params] = fn["iterator"]
return fn["iterator"]()
for i in cFor("i=100;i>2;i=i/2"):
print(i)
100
50.0
25.0
12.5
6.25
3.125
Note that the i variable in the string parameter is internal to the iterator and is not accessible within the for loop's code. We could have written for i in cFor("x=100;x>2;x=x/2") and still use i within the loop
That being said, I would still suggest that you embrace Python's way of doing things and not try to reproduce other language's syntax (i.e. use a while statement in this particular case)
for example:
x = 100
while x > 2:
i,x = x,x/2 # using x for the next value allows this to be placed
# at the beginning of the loop (rather than at the end)
# and avoids issues with the continue statement
print(i)
# ... your code ...
Or, you could use a bit of math:
# 6 = int(math.log(100,2))
for i in [100/2**i for i in range(6)]:
print(i)
# Strangely enough, this is actually slower than the cFor() folly
Here's another approach to handle special progressions in a cleaner and more generic fashion. It is a class that implements (and hides) internal workings of a loop variable.
class Loop:
def __init__(self,start=0):
self._firstPass = True
self._value = start
#property
def value(self): return self._value
def start(self,initial):
if self._firstPass : self._value = initial
return self
def next(self,nextValue=None):
if nextValue is None : nextValue = self.value + self._increment
if self._firstPass : self._firstPass = False
else : self._value = nextValue
return self
def up(self,by=1):
return self.next(self.value+by)
def down(self,by=1):
return self.next(self.value-by)
def upTo(self,last,by=1):
if self._firstPass: self._firstPass = False
else: self._value += by
return self.value <= last
def downTo(self,last,by=1):
if self._firstPass: self._firstPass = False
else: self._value -= by
return self.value >= last
def loop(self,condition=True):
self._firstPass = False
return condition
def until(self,condition=False):
self._firstPass = False
return not condition
def __getitem__(self,index): return self.value[index]
def __str__(self): return str(self.value)
def __int__(self): return int(self.value)
def __float__(self): return float(self.value)
def __add__(self,other): return self.value + other
def __sub__(self,other): return self.value - other
def __mul__(self,other): return self.value * other
def __matmul__(self,other): return self.value.__matmul__(other)
def __divmod__(self,other): return divmod(self.value,other)
def __pow__(self,other): return self.value ** other
def __truediv__(self,other): return self.value / other
def __floordiv__(self,other): return self.value // other
def __mod__(self,other): return self.value % other
def __lshift__(self,other): return self.value << other
def __rshift__(self,other): return self.value >> other
def __lt__(self,other): return self.value < other
def __le__(self,other): return self.value <= other
def __eq__(self,other): return self.value == other
def __ne__(self,other): return self.value != other
def __gt__(self,other): return self.value > other
def __ge__(self,other): return self.value >= other
def __and__(self,other): return self.value & other
def __or__(self,other): return self.value | other
def __xor__(self,other): return self.value ^ other
def __invert__(self): return -self.value
def __neg__(self): return -self.value
def __pos__(self): return self.value
def __abs__(self): return abs(self.value)
def __radd__(self, other): return other + self.value
def __rsub__(self, other): return other - self.value
def __rmul__(self, other): return other * self.value
def __rmatmul__(self, other): return other.__matmul__(self.value)
def __rtruediv__(self, other): return other / self.value
def __rfloordiv__(self, other): return other // self.value
def __rmod__(self, other): return other % self.value
def __rdivmod__(self, other): return divmod(other,self.value)
def __rpow__(self, other): return other ** self.value
def __rlshift__(self, other): return other << self.value
def __rrshift__(self, other): return other >> self.value
def __rand__(self, other): return other & self.value
def __rxor__(self, other): return other ^ self.value
def __ror__(self, other): return other | self.value
The class is designed to work with the while statement after initializing a loop variable. The loop variable behaves like a normal int (or float, or str, etc.) when used in calculations and conditions. This allows the progression and stop condition to be expressed as you would write them for an ordinary loop variable. The class adds a few method to control the loop process allowing for non-standard increments/decrements:
For example:
i = Loop()
while i.start(100).next(i//2).loop(i>2):
print(i) # 100, 50, 25, 12, 6 ,3
# Note: to use i for assignment or as parameter use +i or i.value
# example1: j = +i
# example2: for j in range(+i)
#
# i.value cannot be modified during the loop
You can also give a start value in the constructor to make the while statement more concise. The class also has an until() function to invert the stop condition:
i = Loop(start=100)
while i.next(i//2).until(i<=2):
print(i) # 100, 50.0, 25.0, 12.5, 6.25, 3.125
Finally there are a couple of helper functions to implement the simpler loops (although a for in would probably be better in most cases):
i = Loop()
while i.start(1).upTo(10):
print(i) # 1,2,...,9,10
i = Loop()
while i.upTo(100,by=5):
print(i) # 0,5,10,15,20,...,95,100
i = Loop(100)
while i.down(by=5).until(i<20):
print(i) # 100,95,90,...,25,20
Sorry for all of the code, but for this assignment I am working on, it is necessary due to different references.
This chapter we are working with Binary Trees and Binary Search Trees.
I tested the BinaryTree() and BinarySearchTree() classes with no issue.
My problem is this: we are adding records to a binary search tree as seen in the Student class and the main() function to test the class. According to the assignment:
The Student class has an id and name, getters and setters, a str() function so that you can print a student record, and the operations ==, !=, <=, <, >=, > defined. The comparison operators compare student id's which are intended to be unique. Since we can compare student records, we can now put them into a Binary Search Tree.
My question is, how do I add the records a binary tree if all of the initialized variables are completely different than ones in the BinaryTree() class and the BinarySearchTree() class?
Everytime I run the code, I get errors such as:
AttributeError: 'Student' object has no attribute 'insert'
or
isEmpty() = True
None
isEmpty() = False
None
101(Betty Smith)
101(Betty Smith)
Traceback (most recent call last):
File "/Users/phillipward/Desktop/Lab 10/StudentRecordsTest.py", line 50, in <module>
main()
File "/Users/phillipward/Desktop/Lab 10/StudentRecordsTest.py", line 22, in main
BST.insert(Student(42, "Amy Winchester"))
File "/Users/phillipward/Desktop/Lab 10/BinarySearchTree.py", line 13, in insert
self.getleftChild().insert(data)
File "/Users/phillipward/Desktop/Lab 10/BinarySearchTree.py", line 7, in insert
if(self.isEmpty()):
File "/Users/phillipward/Desktop/Lab 10/binaryTree.py", line 33, in isEmpty
if(self is None or self.data is None):
AttributeError: 'Student' object has no attribute 'data'
I'm stuck on how to tell the program that I the student class is a type of BinarySearchTree.
class BinaryTree():
def __init__(self, data = None, leftChild = None, rightChild = None):
self.data = data
self.leftChild = leftChild
self.rightChild = rightChild
def getData(self):
return self.data
def setData(self, x):
self.data = x
return self.data
def getleftChild(self):
return self.leftChild
def setleftChild(self, x):
self.leftChild = x
return self.leftChild
def getrightChild(self):
return self.rightChild
def setrightChild(self, x):
self.rightChild = x
return self.rightChild
def isEmpty(self):
if(self is None or self.data is None):
return True
else:
return False
def __str__ (self):
return "{0}".format(self.data)
def inOrderTraversal(self):
if (self is None):
return "Empty"
else:
result = ""
if(self.getleftChild() is not None): # When we put payload in, we need equality
result += BinaryTree.inOrderTraversal(self.getleftChild()) + " "
result += str(self.getData())
if (self.getrightChild() is not None):
result += " " + BinaryTree.inOrderTraversal(self.getrightChild()) + " "
return result
def preOrderTraversal(self):
if (self.isEmpty()):
return "Empty"
else:
result = ""
result += str(self.getData())
if (self.getleftChild() is not None):
result += " " + BinaryTree.preOrderTraversal(self.getleftChild()) + " "
if (self.getrightChild() is not None):
result += BinaryTree.preOrderTraversal(self.getrightChild())
return result
def postOrderTraversal(self):
if (self.isEmpty()):
return "Empty"
else:
result = ""
if (self.getleftChild() is not None):
result += BinaryTree.postOrderTraversal(self.getleftChild()) + " "
if (self.getrightChild() is not None):
result += BinaryTree.postOrderTraversal(self.getrightChild()) + " "
result += str(self.getData())
return result
def insert(self, data):
if (self.isEmpty()):
self.setData(data)
elif (data < self.getData()):
if (self.getleftChild() is None):
self.setleftChild(BinarySearchTree(data))
else:
self.getleftChild().insert(data)
else: # data >= self.getData()
if (self.getrightChild() is None):
self.setrightChild(BinarySearchTree(data))
else:
self.getrightChild().insert(data)
def retrieve(self, data):
if self.isEmpty():
return None
elif data == self.getData():
return self.getData()
elif data <= self.getData():
if self.getleftChild() is None:
return None
else:
return self.getleftChild().retrieve(data)
else:
if self.getrightChild() is None:
return None
else:
return self.getrightChild().retrieve(data)
from binaryTree import BinaryTree
class BinarySearchTree(BinaryTree):
def insert(self, data):
if(self.isEmpty()):
self.setData(data)
elif(data < self.getData()):
if(self.getleftChild() is None):
self.setleftChild(data)
else:
self.getleftChild().insert(data)
else: #data >= self.getData()
if(self.getrightChild() is None):
self.setrightChild(data)
else:
self.getrightChild().insert(data)
def retrieve(self, data):
if self.isEmpty():
return None
elif data == self.getData():
return self.getData()
elif data <= self.getData():
if self.getleftChild() is None:
return None
else:
return self.getleftChild().retrieve(data)
else:
if self.getrightChild() is None:
return None
else:
return self.getrightChild().retrieve(data)
def minValue(self):
current = self
while current.leftChild is not None:
current = current.leftChild
return current.data
def maxValue(self):
current = self
while current.rightChild is not None:
current = current.rightChild
return current.data
def isBST(self):
current = self
if current == None:
return True
if current.leftChild.data <= current.data and
current.rightChild.data >= current.data:
return True
else:
return False
class Student():
def __init__(self, id = None, name = ""):
self.__id = id
self.__name = name
def getId(self):
return self.__id
def setId(self, id):
self.__id = id
def getName(self):
return self.__name
def setName(self, name):
self.__id = name
def __str__(self):
if (self is None):
return ""
else:
return str(self.__id) + "(" + self.__name + ")"
def __cmp__(self, other):
if (self is None or other is None):
return 0
else:
return self.__id - other.__id
def __eq__(self, other):
return self.__cmp__(other) == 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __le__(self, other):
return self.__cmp__(other) <= 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __ge__(self, other):
return self.__cmp__(other) >= 0
from BinarySearchTree import BinarySearchTree
from Student import Student
def main():
BST = BinarySearchTree()
print("isEmpty() = " + str(BST.isEmpty()))
print(BST)
BST.setData(Student(101, "Betty Smith"))
print("isEmpty() = " + str(BST.isEmpty()))
print(BinarySearchTree())
BST.insert(Student(50, "John Adams"))
print(BST)
BST.insert(Student(250, "Mike Jones"))
print(BST)
BST.insert(Student(42, "Amy Winchester"))
print(BST)
BST.insert(Student(31, "Jill Ranger"))
print(BST)
BST.insert(Student(315, "Bob Crachet"))
print(BST)
BST.insert(Student(200, "Karen Wilkins"))
print(BST)
print("\n")
print()
print("Inorder traversal: " + str(BST))
print()
print("Preorder traversal: \n" + BST.preOrderTraversal())
print()
print("Postorder traversal: " + BST.postOrderTraversal())
print()
print("minValue: " + str(BST.minValue()))
print("maxValue: " + str(BST.maxValue()))
print()
print("isBST = " + str(BST.isBST()))
for id in [101, 200, 31, 50, 315, 250, 42]:
print(BST.retrieve(Student(id)))
main()
I've created a class called Card, which takes a number and gives the following output depending on the methods called:
class Card:
def __init__(self, number):
self.number = number
def suit(self):
if self.number in range(0, 13):
return 0
elif self.number in range(13, 26):
return 1
elif self.number in range(26, 39):
return 2
elif self.number in range(39, 52):
return 3
def rank(self):
if self.number in range(0, 13):
return self.number
elif self.number in range(13, 26):
return self.number - 13
elif self.number in range(26, 39):
return self.number - 26
elif self.number in range(39, 52):
return self.number - 39
def points(self):
if self.number in (12,25,38,51):
return 4
elif self.number in (11,24,37,50):
return 3
elif self.number in (10,23,36,49):
return 2
elif self.number in (9,22,35,48):
return 1
else:
return 0
def __repr__(self):
ranks = ['2','3','4','5','6','7','8','9','10','J','Q','K','A']
if self.number in range(0, 13):
return ranks[self.number] + '\u2663'
elif self.number in range(13, 26):
return ranks[self.number - 13] + '\u2666'
elif self.number in range(26, 39):
return ranks[self.number - 26] + '\u2665'
elif self.number in range(39, 52):
return ranks[self.number - 39] + '\u2660'
def __lt__(self,other):
if str(self.rank) < str(other.rank):
return True
else:
return False
* any tips on making the code better are appreciated
Now I have to write a class called BlackjackCard with Card class inherited:
class BlackjackCard(Card):
def __init__(self, number):
Card.__init__(self, number)
def points(self):
if self.rank == 12:
return 11
elif self.rank in (11,10,9):
return 10
elif self.rank < 11:
return self.rank
I am trying to overload the method points() by rewriting but I can't seem to implement self.rank from class Card.
When I assign y = BlackjackCard(38) and executey.points(), it gives me a type error: unorderable types: method() < int().
What am I doing wrong here?
self.rank is a method. Either call it by adding parens (self.rank()), or convert it into a property.
Ignacio's answer is the right one (using property), but in terms of improving your code in general, a few suggestions.
First, instead of if foo in range(a, b):, you can just do if a <= foo < b: But in your case, you can simplify this further using math. The suit is just floor(number/13), or more simply number//13. Similarly, the rank is the the remainder of number/13, the modulo of 13, which is number%13.
Second, rather than re-calculate everything every time, you can re-use the result of one method in another. For example, you re-calculate the suit in __repr__.
Finally, boolean tests in python, like x < y, resolve to True or False. So rather than return True if the test passes and False if it doesn't, you can just return the result of the test exactly.
Also, I don't think you want to return the str of the rank in __lt__, but rather the numerical rank.
So here is my improved version:
class Card:
def __init__(self, number):
self.number = number
#property
def suit(self):
return self.number // 13
#property
def rank(self):
return self.number % 13
#property
def points(self):
return max(0, self.rank-8)
#property
def suit_str(self):
suits = ['\u2663', '\u2666', '\u2665', '\u2660']
return suites[self.suit]
#property
def rank_str(self):
ranks = {9: 'J', 10: 'Q', 11: 'K', 12: 'A'}
return ranks.get(self.rank, str(self.rank+2))
def __repr__(self):
return self.rank_str+self.suit_str
def __lt__(self, other):
return self.rank < other.rank
Running the program will cause an error message:
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
The problem is with the line value += values.get(card.get_rank)
I think there may be a problem with the get_rank method? Does it not return an integer?
ranks = ('A','2','3','4','5','6','7','8','9','10','J','Q','K')
values = {'A':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'10':10,'J':10,'Q':10,'K':10}
suits = ('Diamonds','Hearts','Clubs','Diamonds')
class Card:
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
def __str__(self, suit, rank):
print (self.rank + 'of' + self.rank)
def get_rank(self):
return self.rank
class Hand:
def __init__(self):
self.hand = []
def __str__(self):
hand = ''
for card in self.hand:
hand = hand + str(card)
return hand
def get_value(self):
value = 0
aces = 0
for card in self.hand:
if card.get_rank == 'A':
aces += 1
value += values.get(card.get_rank)
if (aces>0) and (value + 10 <= 21):
value += 10
return value
values.get(card.get_rank) tries to use the instance method as the key for the dictionary. This is not a valid key in the dictionary, so dict.get() returns the default None.
Instead you want to call the method, and use the return value as the key:
value += values.get(card.get_rank())
or, as trivial getters and setters are unpythonic, just access the attribute directly:
value += values.get(card.rank)
Note that you can also pass a default to dict.get() to ensure you always get a sensible return value:
value += values.get(card.rank, 0)
Now if there is no value for that card rank in values, its value is assumed to be zero.
Also, it's not clear where values is coming from. I would suggest you make it a class attribute:
class Hand:
VALUES = {...}
...
def get_value(self):
...
value += self.VALUES.get(card.rank, 0)
...
Or an explicit argument to get_value:
class Hand:
...
def get_value(self, values):
...
value += self.values.get(card.rank, 0)
...