I got problem a about to call func from class - python

I got this code from a book name 'Data Structures and Algorithms in Python from Michael T. Goodrich ' chapter 5.5.1 Storing High Scores for a Game.
Can some one can explain to me what this self._board[-1].get_score( ) means ??
I try to print it to see what happens but I got:
print(self._board[-1].get_score( ))
AttributeError: 'NoneType' object has no attribute 'get_score'
class GameEntry:
def __init__(self, name, score):
self._name = name
self._score = score
def get_name(self):
return self._name
def get_score(self):
return self._score
def __str__(self):
return ('({0}, {1})'.format(self._name, self._score)) # e.g., (Bob, 98)
class Scoreboard():
def __init__(self, capacity=10):
self._board = [None]*capacity # reserve space for future scores
self._n=0 # number of actual entries
def __getitem__(self, k):
return self._board[k]
def add(self, entry):
score = entry.get_score()
print(score)
if self._n < len(self._board) or score > self._board[-1].get_score(): # what is it " self._board[-1].get_score()"
if self._n < len(self._board): # no score drops from list
self._n += 1 # so overall number increases
j = self._n - 1
while j > 0 and self._board[j-1].get_score( ) < score:
self._board[j] = self._board[j-1] # shift entry from j-1 to j
j -= 1 # and decrement j
self._board[j] = entry
def __str__(self):
return '\n' .join(str(self._board[j]) for j in range(self._n))
a_ = Scoreboard()
a = ('a','b','c','d')
b = (5,4,8,4)
c = dict(zip(a,b))
print(c)
for i,j in c.items():
x = GameEntry(i,j)
print(x)
y=a_.add(x)
print(y)

Inside your class Scoreboard you keep a list of game entries:
self._board = [None]*capacity # reserve space for future scores
This list is used to keep GameEntry entries:
self._board[j] = entry
The logic in your application uses ._n to track the number of entries added, but only up to total number of score slots available (self._n < len(self._board)).
If this is False (i.e. the number of entries added is the same as the capacity of the entry list when it was initialized), the other part of the or statement gets executed:
score > self._board[-1].get_score()
This looks at the last element in self._board - i.e. what is supposed to be the slot entry with the lowest score to see if this score deserves to be entered into the list instead of the previous one. Any negative list index starts from the end of the list instead of the wrong, so -1 points to the last entry, -2 the second to last entry, and so on.
If you get a None entry while running your code (and not just printing it out before you've added ten entries) is another question, and is probably what you need to debug further (but it doesn't seem to be the case from your code). If you attempt to print that entry before inserting all ten entries (i.e. filling up the entry list), there won't be an entry in last place, and the None placeholder will still be there.

Related

Adding and Deleting Values from Dictionary through Object Oriented Programming - Python

I am trying to create a Python program which has the details of power grids and the houses connected to each of those power grids.
I have mainly two tasks to handle, one to add houses to the connection list of each power grid and remove them.
Following is the class for my house:
class House:
def __init__(self, number, power):
self.house_no = number
self.power_required = power
self.assigned_propagator_no = -1
def assign_propagtor(self, number):
self.assigned_propagator_no = number
def __str__(self):
return f"house_no: {self.house_no} power_required: {self.power_required}"
I have a propagator class which is basically used to store all the details and connect between the houses and the power grids:
class Propagator:
def __init__(self, number, max_power):
self.propagator_no = number
self.maximum_power = max_power
self.power_remaining = max_power
self.houses_connected = list()
def __str__(self):
return f"no : {self.propagator_no} max_power: {self.maximum_power} power_remaining: {self.power_remaining}"
def connected_houses_info(self):
for house,_ in self.houses_connected:
print(house)
# check if the house is connected part of the list or not.
def is_house_present(self, house_no):
if house_no in self.houses_connected:
return self.houses_connected.index(house_no)
else:
return -1
# Add the house in the list.
# Before adding check if the house is already connected
def add_house(self, house:House):
if house.assigned_propagator_no != -1:
print('Already exists!')
else:
self.houses_connected.append(house.house_no)
# Remove the house from the list, before removing need to check
# if the house is in the assigned propoagtor list.
def remove_house(self, house_no:int):
pass
Similarly I have created the class for my power grid:
class PowerGrid:
def __init__(self):
self.propagators = dict()
# Adding the propagtor into in the dictionary.
# Check if the propagator is not part of the dictioary already
# It will not posess any value in the beginning.
def add_propagator(self, propagator:Propagator):
pass
# Removing the propagtor into in the dictionary.
# Check if the propagator is part of the dictioary or not
def remove_propagator(self, propagator_no):
pass
def add_house(self, house:House, propagator_no:int):
if propagator_no in self.propagators:
return self.propagators[propagator_no].add_house(house)
else:
return False
def remove_house(self, house_no:int, propagator_no:int):
if propagator_no in self.propagators:
return self.propagators[propagator_no].remove_house(house_no)
else:
return False
class Propagator:
def __init__(self, number, max_power):
self.propagator_no = number
self.maximum_power = max_power
self.power_remaining = max_power
self.houses_connected = list()
def __str__(self):
return f"no : {self.propagator_no} max_power: {self.maximum_power} power_remaining: {self.power_remaining}"
def connected_houses_info(self):
for house,_ in self.houses_connected:
print(house)
# check if the house is connected part of the list or not.
def is_house_present(self, house_no):
if house_no in self.houses_connected:
return self.houses_connected.index(house_no)
else:
return -1
# Add the house in the list.
# Before adding check if the house is already connected
def add_house(self, house:House):
pass
# Remove the house from the list, before removing need to check
# if the house is in the assigned propoagtor list.
def remove_house(self, house_no:int):
pass
The main function to create the power grid is as follows:
def create_power_grid():
power_grid = PowerGrid()
with open('app.csv', newline='') as csvfile: # app.csv image provided later
reader = csv.DictReader(csvfile)
for row in reader:
entity_type = row['Type']
if entity_type == "propagator":
propagator = Propagator(int(row['Entity_Number']), int(row['Power']))
power_grid.add_propagator(propagator)
elif entity_type == "house":
house = House(int(row['Entity_Number']), int(row['Power']))
house.assigned_propagator_no = int(row['Assigned_Propagator'])
power_grid.add_house(house, int(row['Assigned_Propagator']))
return power_grid
if __name__ == "__main__":
power_grid = create_power_grid()
for _, propagator in power_grid.propagators.items():
#Printing the propagator information
print(f"Propagator No : {propagator.propagator_no}")
print("------------Propagator Information-----------------")
print(propagator)
print("------------Connected Houses Information-----------------")
propagator.connected_houses_info()
print("\n")
print("----Removing House 1014 from Propagator 1002----")
if power_grid.remove_house(1014, 1002):
print("House 1014 is successfully removed from Propagator 1002")
else:
print("House 1014 is not connected with Propagator 1002")
print("\n----Removing House 1024 from Propagator 1003----")
if power_grid.remove_house(1024, 1003):
print("House 1024 is successfully removed from Propagator 1003")
else:
print("House 1024 is not connected with Propagator 1003")
I have made certain changes to the add_propagator function inside class PowerGrid as follows:
def add_propagator(self, propagator:Propagator):
if (propagator.propagator_no not in self.propagators.keys()):
self.propagators[propagator.propagator_no] = {}
self.propagators[propagator.propagator_no]['max_power'] = propagator.maximum_power
self.propagators[propagator.propagator_no]['houses'] = []
But I am getting an error:
AttributeError: 'dict' object has no attribute 'add_house'
for the following part:
def add_house(self, house:House, propagator_no:int):
if propagator_no in self.propagators:
return self.propagators[propagator_no].add_house(house)
else:
return False
I have a CSV file from which the values are fed in. The format of the CSV file looks like the following:
I am unable to understand how to define the addition of a house and removal of a house from this, any kind of help with explanation is appreciated.
To have a key correspond to a list in a dictionary in python, note that d[item] is a list::
d = dict()
for item in items_to_add:
if item not in d:
d[item] = []
d[item].append(the_other_item)
# in your case:
if propagator_no not in self.propagators:
self.propagators[propagator_no] = []
self.propagators[propagator_no].append(house)
Your problem is that self.propogators is a dictionary, and therefore has these functions, not add_house.

Why is this returning a pop index out of range error?

So I'm trying to simulate the NBA lottery and when I create an array of teams and attempt to assign each team its combinations within a for loop, I get a pop index out of bounds error in this line:
combos.append(self.combinations.pop(randint(0,len(self.combinations))))
However, if I simply assign one team item its combinations, no error occurs. The error occurs when I try to assign its combinations iteratively.
from Team import *
from LotteryMachine import *
"""Main class"""
lotteryMachine = LotteryMachine()
"""Initialize Teams"""
teams = [Team('Lakers', '81-0',1),Team('Wizards', '81-0',2)]
for team in teams:
team.combinations=
lotteryMachine.assignCombinations(team.numCombinations)
Team class:
class Team():
combination_dict={1:250, 2:199, 3:156, 4:119, 5:88, 6:63, 7:43, 8:28, 9:17, 10:11, 11:8, 12:7, 13: 6, 14:5}
def __init__(self, name, record, standing):
self.name=name
self.record = record
self.standing = standing
self.numCombinations= Team.combination_dict[self.standing]
def __str__(self):
return self.name
Lottery Machine Class:
from random import *
class LotteryMachine():
def __init__(self):
self.combinations = list(self.createCombinations([x for x in range(1,15)],4))
self.deleteRandom()
self.copyCombinations = self.combinations
def combination(self,n,k):
return int(self.factorial(n)/(self.factorial(k)*self.factorial(n-k)))
def factorial(self,n):
assert n>=0
if n==0 or n==1:
return 1
return n*self.factorial(n-1)
def createCombinations(self,elements,length):
for i in range(len(elements)):
if length==1:
yield [elements[i],]
else:
for next in self.createCombinations(elements[i+1:len(elements)],length-1):
yield [elements[i],]+next
def deleteRandom(self):
self.combinations.pop(randint(0,len(self.combinations)))
def assignCombinations(self,length):
combos=[]
for x in range(length):
combos.append(self.combinations.pop(randint(0,len(self.combinations))))
return combos
#error occurs in above line
def drawCombinations(self):
combos=[]
for x in range(3):
combos.append(self.copyCombinations.pop(randint(0, len(self.copyCombinations))))
return combos
def __str__(self):
return "NBA Lottery Machine"
The randint function returns a random integer from a range up to and including the upper bound. If you want it to generate random indexes into a list, you probably want randrange instead, which selects from a half-open interval (including the lower bound, but excluding the upper bound).

NameError: name 'self' is not defined at the last block only?

Why it tells me "NameError: name 'self' is not defined" when I run this code?
I don't know why it's all fine except the last block (update method) gives me that error anyone can help?
This code is a game that takes in the lenth of the hand and gives you a random letters to create a word from the hand ,when you enter an currect word, the letters used in the word get removed automaticlly till the words
import random
class Hand(object):
self.hand = {}
def __init__(self, n):
'''
Initialize a Hand.
n: integer, the size of the hand.
'''
assert type(n) == int
self.HAND_SIZE = n
self.VOWELS = 'aeiou'
self.CONSONANTS = 'bcdfghjklmnpqrstvwxyz'
# Deal a new hand
self.dealNewHand()
def dealNewHand(self):
'''
Deals a new hand, and sets the hand attribute to the new hand.
'''
# Set self.hand to a new, empty dictionary
self.hand = {}
# Build the hand
numVowels = self.HAND_SIZE // 3
for i in range(numVowels):
x = self.VOWELS[random.randrange(0,len(self.VOWELS))]
self.hand[x] = self.hand.get(x, 0) + 1
for i in range(numVowels, self.HAND_SIZE):
x = self.CONSONANTS[random.randrange(0,len(self.CONSONANTS))]
self.hand[x] = self.hand.get(x, 0) + 1
def setDummyHand(self, handString):
'''
Allows you to set a dummy hand. Useful for testing your implementation.
handString: A string of letters you wish to be in the hand. Length of this
string must be equal to self.HAND_SIZE.
This method converts sets the hand attribute to a dictionary
containing the letters of handString.
'''
assert len(handString) == self.HAND_SIZE, "Length of handString ({0}) must equal length of HAND_SIZE ({1})".format(len(handString), self.HAND_SIZE)
self.hand = {}
for char in handString:
self.hand[char] = self.hand.get(char, 0) + 1
def calculateLen(self):
'''
Calculate the length of the hand.
'''
ans = 0
for k in self.hand:
ans += self.hand[k]
return ans
def __str__(self):
'''
Display a string representation of the hand.
'''
output = ''
hand_keys = sorted(self.hand.keys())
for letter in hand_keys:
for j in range(self.hand[letter]):
output += letter
return output
def update(self, word):
"""
Does not assume that self.hand has all the letters in word.
Updates the hand: if self.hand does have all the letters to make
the word, modifies self.hand by using up the letters in the given word.
Returns True if the word was able to be made with the letter in
the hand; False otherwise.
word: string
returns: Boolean (if the word was or was not made)
"""
for i in word :
if self.hand.get(i , 0) == 0 :
return False
else :
self.hand[i] -=1
return True
I think it's this line:
self.hand = {}
at the top of your class that is the problem. There is no self at the point when the class is being defined. This line should be inside your __init__ method.
def __init__(self, n):
'''
Initialize a Hand.
n: integer, the size of the hand.
'''
self.hand = {}
# ... and the rest

Add elements into a sorted array in ascending order

I have the following program which implements a sorted bag. It adds elements
successfully in sorted order (ascending order) when giving a list.
When I created a new sorted bag with the argument of another bag, it's not in sorted order (instead in descending order). See below
Thanks for the help
# Array Class
#----------------------------------------------------
class Array(object): # Represents an array.
DEFAULT_CAPACITY = 5
def __init__ (self, capacity, fillValue = None):
'''Capacity = static size of array. fillValue is placed at each element'''
self._items = list()
self._capacity = capacity
self._logicalSize = 0
self._fillValue = fillValue
for count in range(capacity):
self._items.append(fillValue)
def __getitem__(self, index): return self._items[index]
def __setitem__(self, index, newItem):
self._items[index] = newItem
# ArraySortedBag Class
#----------------------------------------------------
class ArraySortedBag(object):
'''An array-based bag implementation'''
def __init__(self, sourceCollection = None):
'''Sets the initial state of self, which includes the contents
of sourceCollection, if it's present'''
self._items = Array(10)
self._size = 0
if sourceCollection:
for item in sourceCollection:
self.add(item)
def __len__(self): return self._size
def __iter__(self):
cursor = 0
while cursor < len(self):
yield self._items[cursor]
cursor += 1
def add(self, item):
'''Adds item to self.'''
insertIndex = 0
# First found the index where the item will be inserted at
for i in range(self._size):
if self._items[i] > item:
insertIndex = i
break
# Then, shift items down by one position until the insertIndex,
for i in range (self._size, insertIndex, -1):
self._items[i] = self._items[i-1]
# Last, assign value to _items[insertIndex]
self._items[insertIndex] = item
self._size += 1
# Test Driver
#----------------------------------------------------
if __name__ == "__main__":
b1 = ArraySortedBag([2000, 2, 1000])
print ("Display bag b1")
for i in b1: # <------ Correct order, ascending order
print (i)
b2 = ArraySortedBag(b1)
print ("\nDisplay bag b2")
for i in b2: # <----- Wrong order, descending order
print (i)
In the second instantiation of class ArraySortedBag, you are passing a sorted list. The ArraySortedBag.init() method adds the items using the add() method. When add() is called the item to be added is never less than the existing list. Therefore insertIndex remains equal to zero. Therefore the new item is added to the beginning of the list.
# First found the index where the item will be inserted at
for i in range(self._size):
if self._items[i] > item: # item is never less than self._items[i]
insertIndex = i
break
For adding items into a sorted list so that one maintains the list sorted, I recommend using the bisect library's insort_left or insort_right.
import bisect
list = [10, 20, 30]
bisect.insort(list, 25)
bisect.insort(list, 15)
print list

Python greedy thief

Why is it giving me an error " 'int' object is not subscriptable " when i run the program? I looked if i was doing anything wrong, i understand it has to be an integer on line 24, but when I'm changing capacity[1] to capacity(int[1]) , it gives me the same error. Any hint would be appreciated.
class Bag():
__slots__=('name', 'weight', 'value')
def mkBag(name, weight, value):
thisBag = Bag()
thisBag.name = name
thisBag.weight = weight
thisBag.value = value
return thisBag
def ratio(treasure):
print(treasure)
print(treasure)
return treasure[2]//treasure[1]
def plunder(treasure, capacity):
treasure = sorted(treasure, key=ratio, reverse=True)
bagLst = []
current = 0
while current < capacity:
if capacity != 0:
if capacity > current[1]:
bagLst.append(mkBag(treasure[0],weight[1],current[2]))
capacity = capacity - current[1]
else:
bagLst.append(mkBag(current[0], capacity, (current[2]/current[1]), capacity))
capacity = 0
return bagLst
def main():
capacity = 10
name = ''
weight = 0
value = 0
treasure = [('silver', 20, 100), ('platinum', 10, 400), ('paladium',10,800), ('diamonds',5,900), ('gold', 10,60)]
bagLst = plunder(treasure, capacity)
for line in bagLst:
print('bagLst')
current is an int:
current = 0
but you are trying to use it as a list:
if capacity > current[1]:
bagLst.append(mkBag(treasure[0],weight[1],current[2]))
capacity = capacity - current[1]
else:
bagLst.append(mkBag(current[0], capacity, (current[2]/current[1]), capacity))
everywhere you use current[index] you are trying to index the integer value.
If you expected current to be a sequence instead, you'd need to set it to one.
I suspect you want to inspect the current treasure to add to the bag; you didn't pick any treasure item however. Something along the lines of:
current = 0
while capacity and current < len(treasure):
item = treasure[current]
current += 1
if capacity > item[1]:
bagLst.append(mkBag(item[0], item[1], item[2]))
capacity = capacity - item[1]
else:
bagLst.append(mkBag(item[0], capacity, (item[2]/item[1]), capacity))
capacity = 0
"int" object not subscriptable means you're trying to do 1234[1]. That doesn't make any sense! You can subscript a string ('abcdefg'[1] == 'b') and a list ([1,2,3,4,5][1] == 2) but you can't get the "nth element" of an integer.
In your line:
# in def plunder(...):
if capacity > current[1]:
You're trying to access the 2nd element of current, which is currently equal to the integer 0. Are you trying to make that a list? What are you expecting to be in current[1]?
Here's a substantially better way to accomplish this
Hey there, so I figured you meant that current[1] was actually item[1], meaning the weight of the item you were looking at. Instead, current was intended to be the running-weight of the bag. Understood! That said, I wrote up a better solution for this: take a look see!
class Treasure(object):
def __init__(self,name,weight=0,value=0,id_=0):
self.name = name
self.weight = weight
self.value = value
self.id = id_ # bootstrap for further development
#property
def ratio(self):
return self.value/self.weight
class BagFullError(ValueError):
pass
class Bag(object):
def __init__(self,owner=None,capacity=10):
self.owner = owner
self.capacity = capacity
self.contents = list()
def __str__(self):
return_value = "CONTENTS:"
for item in self.contents:
return_value += "\n ${0.value:4} {0.name:10}{0.weight} lb".format(item)
return return_value
def add(self,other):
if not isinstance(other,Treasure):
raise TypeError("Must pick up Treasure")
if self.weight + other.weight > self.capacity:
raise BagFullError("Bag cannot fit {}({} lb) ({} lb/{} lb)".format(
other.name,other.weight,self.weight,self.capacity))
self.contents.append(other)
def remove(self,other):
self.contents.remove(other)
# may throw ValueError if `other` not in `self.contents`
#property
def weight(self):
return sum(item.weight for item in self.contents)
treasure = [Treasure('silver', 20, 100), Treasure('platinum', 10, 400),
Treasure('paladium',10,800), Treasure('diamonds',5,900),
Treasure('gold', 10,60)]
## map(lambda x: Treasure(*x), [('silver',20,100), ... ])
def plunder(treasure_list,bag=None):
_bag = bag or Bag()
treasures = sorted(treasure_list,
key = lambda x: x.ratio,
reverse = True)
while True:
for treasure in treasures:
try: _bag.add(treasure)
except BagFullError as e:
print(e)
return _bag
bag = Bag("Adam",100)
print(bag)
plunder(treasure,bag)
print(bag)
print("Total Value: {}".format(sum(item.value for item in bag.contents)))

Categories

Resources