Python Class Always Return None - python

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)

Related

Nested data in attribute is not accessible

I have two classes (Student and Course). I'm trying to write a method for the Course class that will remove a given student from a course. However, there's a problem when I run
self.students.remove(student) in the method. The error tells me that student is not in the students list. Printing the students list I don't actually see the values, but instead I see a reference to it:
print(self.students)
> [<data.Student object at 0x7fc9980334f0>, <data.Student object at 0x7fc998033580>, <data.Student object at 0x7fc9980428b0>, <data.Student object at 0x7fc998042a00>]
However, if I select a specific student at an index then I'm able to see the actual data.
print(self.students[0])
> 2020411:King,Maha
Why is this happening when trying to print the students attribute?
Code if needed:
from copy import deepcopy
class Student:
def __init__(self, sid, last, first):
self.sid = sid
self.last = last
self.first = first
def __str__(self):
return '{}:{},{}'.format(self.sid, self.last, self.first)
def __repr__(self):
return '{}:{},{}'.format(self.sid, self.last, self.first)
class Course:
def __init__(self, crn, students):
self.crn = crn
self.students = deepcopy(students)
def key(self):
return self.crn
def is_empty(self):
return len(self.students) == 0
def get_student(self, student_key):
for student in self.students:
if student.key() == student_key:
return deepcopy(student)
return None
def __contains__(self, student):
for i in self.students:
if student.key() == i.key():
return True
break
return False
def register(self, student):
if student not in self:
self.students.append(deepcopy(student))
return
def drop(self, student):
s = None
if student in self:
s = deepcopy(student)
self.students.remove(student)
return s
student1 = Student(2020411, 'King', 'Maha')
student2 = Student(2019399, 'Hess', 'Alvin')
student3 = Student(2020301, 'Chin', 'Yu')
student4 = Student(2019111, 'Hay', 'Ria')
student_list = [student1, student2, student3]
course1 = Course('CP104', student_list)
removed_student = course1.drop(student2)
The issue with deepcopy() is that it creates an entirely new object that has the same attributes as the original one, yet they are not equal. For list.remove(), this compares the reference to check if the actual object exists. In your case, you are trying to remove an object that is not in the list.
Instead of removing it, if you want to return the student, use list.pop().
def drop(self, student):
for i, s in enumerate(self.students):
if s.sid == student.sid :
return self.students.pop(i)
As a side note, it will be easier to do operations if Course.students is a dictionary such that:
self.students = {
`sid1`: student1,
`sid2`: student2,
# etc
}
EDIT: Alternatively, implement __eq__() in Student so that list.remove() will work.
def __eq__(self, other):
return self.sid == other.sid and self.first == other.first and self.last == other.last

Finding min of different counts in object

Im trying to write a python method that looks at Rarity and counts the different types and then returns what the smallest count is but i keep getting: 'MagicCard' object is not subscriptable. Im also wanting to check which colors are associated with the least rare cards. Any help would be appreciated:
Here is where the Json can be obtained from for testing: http://mtgjson.com/json/DTK.json
Card Object:
class MagicCard(object):
def __init__ (self, jsonCard):
self.name=jsonCard["name"]
if jsonCard.get("colors",""):
self.colors=jsonCard["colors"]
else:
self.colors=None
if jsonCard["rarity"]:
self.rarity=jsonCard["rarity"]
else:
self.rarity=None
def get_name(self):
"""Return the name of the card"""
return self.name
def get_colors(self):
"""Return the colors of the card"""
return self.colors
def get_rarity(self):
"""Return the rarity of the card"""
return self.rarity
Card Deck Object:
class MagicCardSet(object):
def __init__(self, DeckDict):
self.cardlist = [MagicCard(eachCard) for eachCard in DeckDict["cards"]]
def get_card_list(self):
Card_name_list=[]
for newCard in self.cardlist:
Card_name_list.append(newCard.get_name())
return(Card_name_list)
def get_card_color(self):
color_list=[]
for newCard in self.cardlist:
color_list.append(newCard.get_color())
return(color_list)
def get_card_rarity(self):
rarity_list=[]
for newCard in self.cardlist:
rarity_list.append(newCard.get_rarity())
return(rarity_list)
def get_rarest_card(self):
for eachCard in self.cardlist:
if eachCard["rarity"]=="Uncommon":
uncommon_counter = uncommon_counter + 1
elif eachCard["rarity"]=="Common":
common_counter=common_counter + 1
elif eachCard["rarity"]=="Rare":
rare_counter = rare_counter + 1
elif eachCard["rarity"]=="Mythic Rare":
mythic_rare_counter = mythic_rare_counter + 1
return(mythic_rare_counter)
error:
The
for eachCard in self.cardlist
returns a MagicCard instance. Call get_rarity() and get_name() and get_colors() on eachCard.
Also, to get the colors of the least rare (most common) cards:
unrare_colors = {}
for eachCard in self.cardlist:
if eachCard.get_rarity() == "Common":
for eachColor in eachCard.get_colors():
unrare_colors[eachColor] = 1
for color in unrare_colors.keys():
print(color)

Blackjack Python

I am trying to figure out how to make the values of t, j, q, and k to the int value of 10. Can someone explain where I went wrong with this?
class Card:
def __init__(self, value , suit):
self.value = value
self.suit = suit
def __repr__(self):
return "The " + self.value + " of " + self.suit
def intValue(self):
if int(self.value) > 2 or int(self.value) < 9:
return self.value
elif str(self.value) == 'a':
return 1
elif str(self.value) == 'j' or str(self.value) == 'q' or str(self.value) == 'k' or str(self.value) == 't':
return 10
Although I agree with the comments about using a dictionary to represent the values, let's fix what you have. It's mostly a matter of keeping it simple and remembering the type of your data (str) and not randomly imposing str() and int() calls when they're not needed:
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit
def __repr__(self):
return "The {} of {}".format(self.value, self.suit)
def intValue(self):
if self.value.isdigit():
return int(self.value)
elif self.value == 'a':
return 1
else:
return 10

Inheritances and overloading methods

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

OO Black Jack Game - computing hand values

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)
...

Categories

Resources