Inheritances and overloading methods - python

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

Related

creat a class for complex number, I got error 'TypeError: __str__ returned non-string (type NoneType) '

as a beginner, I met the below problem, I created a class for complex number calculation. the problem is from the str method. can you tell me the reason and how can I solve it? thank you!
import math
class Complex(object):
def __init__(self, real, imaginary):
self.real= real
self.imaginary = imaginary
def __add__(self, other):
return Complex(self.real + other.real, self.imaginary + other.imaginary)
def __sub__(self, other):
return Complex(self.real - other.real, self.imaginary - other.imaginary)
def __mul__(self, other):
return Complex(self.real*other.real-self.imaginary*other.imaginary,self.real*other.imaginary+self.imaginary*other.real)
def __truediv__(self, other):
r=(self.real*other.real+self.imaginary*other.imaginary)/(other.real**2+other.imaginary**2)
img=(self.imaginary*other.real-self.real*other.imaginary)/(other.real**2+other.imaginary**2)
return Complex(r,img)
def mod(self):
return Complex((self.real**2+self.imaginary**2)**0.5, 0)
def __str__(self):
if self.imaginary == 0:
result = "%.2f+0.00i" % (self.real)
elif self.real == 0:
if self.imaginary >= 0:
result = "0.00+%.2fi" % (self.imaginary)
else:
result = "0.00-%.2fi" % (abs(self.imaginary))
elif self.imaginary > 0:
result = "%.2f+%.2fi" % (self.real, self.imaginary)
else:
result = "%.2f-%.2fi" % (self.real, abs(self.imaginary))
if __name__ == '__main__':
c = map(float, input().split())
d = map(float, input().split())
x = Complex(*c)
y = Complex(*d)
print(*map(str, [x+y, x-y, x*y, x/y, x.mod(), y.mod()]), sep='\n')
The __str__() method should return the strings, not assign them to a variable.
def __str__(self):
if self.imaginary == 0:
return "%.2f+0.00i" % (self.real)
elif self.real == 0:
if self.imaginary >= 0:
return "0.00+%.2fi" % (self.imaginary)
else:
return "0.00-%.2fi" % (abs(self.imaginary))
elif self.imaginary > 0:
return "%.2f+%.2fi" % (self.real, self.imaginary)
else:
return "%.2f-%.2fi" % (self.real, abs(self.imaginary))

Python Class Always Return None

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)

Is there any method like divide by or multiply by in python range()?

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

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

Using an iterator in python?

I have just learned about iterators in Python however I am having a hard time implementing them.
I am trying to write a class to so that this loop works:
odds = OddNumbers(13)
for i in odds:
print(i)
I want to write an iter() function and next() function to do this.
So far I have:
class OddNumbers:
def __init__(self, number):
self.number = number
def __iter__(self):
return self
def __next__(self):
current = self.number
if self.number%2 == 0:
return current
else:
raise StopIteration
But at the moment this is returning nothing. I expect the output to be
1
3
5
7
9
11
13
Help?
Your object needs to keep track of its state and update it when __next__ is called.
class OddNumbers(object):
def __init__(self, number):
self.current = ...
self.number = number
def __iter__(self):
return self
def __next__(self):
# Update self.current
# If the limit has been reached, raise StopIteration
# Otherwise, return something
You need another variable to track the current number:
def __init__(self, number):
self.number = number
self.current = 1
Then you need to compare it with the ending number, and maybe increment it:
def __next__(self):
if self.current > self.number:
raise StopIteration
current = self.current
self.current += 2
return current
There is probably a much cleaner way of doing this, but here is a quick stab:
class OddNumbers:
def __init__(self, number):
self.number = number
def __iter__(self):
self.current = self.number
return self
def __next__(self):
if self.current > 0:
if self.current % 2 == 0:
self.current -= 1
self.current -= 1
return self.current + 1
raise StopIteration
This will give you an iterator-like object which provides even or odd numbers. However, it won't satisfy your for loop semantics as it is not a true iterator.
class NumberIterator(object):
"""Returns simple even/odd number iterators."""
def __init__(self, current):
self.current = current
def next(self):
self.current += 2
return self.current
#classmethod
def getOddIterator(cls):
return cls(-1) # start at 1
#classmethod
def getEvenIterator(cls):
return cls(0) # start at 2
odd_numbers = NumberIterator.getOddIterator()
even_numbers = NumberIterator.getEvenIterator()
odd_numbers.next() # Returns 1
odd_numbers.next() # Returns 3

Categories

Resources