Python libtcod weird description error - python

I'm surprised this isn't crashing, but I'm really not sure what's happened here. On mouse over + key press 'd' should bring up menu of objects under mouse (which it does). When selecting an object it should print a message to console from that objects description. It doesn't though, it prints something like <__main__.Object instance at 0x02AE1800>.
Here is the relevant code
def menu(header, options, width):
if len(options) > 26: raise ValueError('Cannot have a menu with more than 26 options.'
#calculate the total height for the header (afer auto wrap) and one line per option
header_height = libtcod.console_get_height_rect(con, 0, 0, width, SCREEN_HEIGHT, header)
height = len(options) + header_height
#create an off-screen console that represents the menu's window
window = libtcod.console_new(width, height)
#print the header, with auto wrap, baby.
libtcod.console_set_default_foreground(window, libtcod.white)
libtcod.console_print_rect_ex(window, 0, 0, width, height, libtcod.BKGND_NONE, libtcod.LEFT, header)
#print all the options
y = header_height
letter_index = ord('a')
for option_text in options:
text = '(' + chr(letter_index) + ') ' + option_text
libtcod.console_print_ex(window, 0, y, libtcod.BKGND_NONE, libtcod.LEFT, text)
y += 1
letter_index += 1
#blit the contents of "window" to the root console
x = SCREEN_WIDTH/2 - width/2
y = SCREEN_HEIGHT /2 - height/2
libtcod.console_blit(window, 0, 0, width, height, 0, x, y, 1.0, 0.7)
#present the root console to the player and wait for keypress
libtcod.console_flush()
key = libtcod.console_wait_for_keypress(True)
#convert the ASCII code to an index; if it corresponds to an option, return it
index = key.c - ord('a')
if index >= 0 and index < len(options): return index
return None
def handle_keys():
global keys;
if key_char == 'd':
#show the description menu, if an item is selected, describe it.
chosen_object = description_menu('Press the key next to an object to see its description.\n')
if chosen_object is not None:
chosen_object.describe()
else:
return 'cancelled'
return 'didnt-take-turn'
def description_menu(header):
global mouse
#return a string with the names of all objects under the mouse
(x, y) = (mouse.cx, mouse.cy)
#create a list with the names of all objects at the mouse's coordinates and in FOV
names = [obj for obj in objects if obj.x == x and obj.y == y and libtcod.map_is_in_fov(fov_map, obj.x, obj.y) and obj.description is not None]
#show a menu with each object under the mouse as an option
if len(names) == 0:
options = ['There is nothing here.']
else:
options = [object.name for object in names]
index = menu(header, options, INVENTORY_WIDTH)
#if an item was chosen, return it
if index is None or len(names) == 0: return None
return names[index]
Class Object:
#this is a generic object: the player, a monster, an item, the stairs...
#it's always represented by a character on screen.
def __init__(self, x, y, char, name, color, blocks=False, fighter=None, ai=None, item=None, description=None):
self.x = x
self.y = y
self.char = char
self.name = name
self.color = color
self.blocks = blocks
self.fighter = fighter
if self.fighter: #let the fighter component know who owns it
self.fighter.owner = self
self.ai = ai
if self.ai: #let the ai component know who owns it
self.ai.owner = self
self.item = item
if self.item: #let the item component know who owns it, like a bitch
self.item.owner = self
self.description = self
if self.description: #let the description component know who owns it
self.description.owner = self
def describe(self):
#describe this object
if self.description is None:
message('The ' + self.owner.name + ' cannot be described.')
else:
message(str(self.description), libtcod.white)`

My guess would be that you are passing a string representation of the Object instance to message when you really want to be passing a string that carries some easy-to-understand information. The str method calls Object.__str__ (one of python's magic methods) Object.__str__ has not been defined in your code, so str defaults to calling Object.__repr__ which is where you are getting the <__main__.Object instance at 0x02AE1800>.
Try defining Object.__str__ so that it returns a string holding the information you would want it to display when print-ing it.
For example:
Class Object:
def __init__(self, name, x, y):
self.name = name
self.x = x
self.y = y
def __str__(self):
return("Object " + self.name + " is at position [" + self.x + "," + self.y + "]")
In the end, I can't say for sure what the problem is because we don't have the definition of the message method. you should include the code for that method so that we can better understand what is going on. Good luck!

Related

Implementing Breadth first search

Your friend bought you a present for the New Year, it's a puzzle! The puzzle consists of a number of
wooden rectangular pieces of varying lengths and widths and a board. The goal is to position the
wooden pieces on the board in a way such that all of the pieces will fit.
I have this program and I need help fixing my breadth first search algorithm.
Right now it is very slow and using a lot of memory. I think it is because I deep copy multiple times. The solve function is the main function and will do the heavy work.
I added a text file that has the first line as the dimensions of the puzzle and the rest of the lines are pieceID, pieceWidth and pieceLength respectively.
This is the Input File. Thank you so much.
10,10
1,10,1
2,1,10
3,1,5
4,3,5
5,20,2
6,1,5
7,1,5
8,2,5
import argparse, copy
import queue
import copy
import numpy as np
class PuzzleBoard():
def __init__(self, board_length, board_width ):
self.l = board_length
self.w = board_width
self.state = [[0 for _ in range(board_width)] for _ in range(board_length)]
self.used_piece = []
# Input: point - tuple cotaining (row_index, col_index) of point in self.state
# Returns true if point is out of bounds; otherwise, returns false
def __out_of_bounds(self, point):
# TODO: Implement this function
if(point < 0 or point > (len(self.state)) or (point > (self.state[0]))):
return True
return False
# Finds the next available open space in the PuzzleBoard (looking from the top-left in row-major order)
def __next(self):
for i in range(len(self.state)) :
for j in range(len(self.state[0])):
if (self.state[i][j] == 0):
return (i, j)
return False
# Input: piece - PuzzlePiece object
# Check if piece fits in the next available space (determined by __next method above)
def fits(self, piece):
position = self.__next()
if not position:
return False
#TODO: Check if any part of the piece is out of bounds
#if piece will be out bounds when place rotate to see if that helps
if((( piece.w + position[0] ) > len( self.state )) or (( piece.l + position[1] )> len( self.state[0] ))):
piece.rotate()
if((( piece.w + position[0] ) > len( self.state )) or (( piece.l + position[1] )> len( self.state[0] ))):
return False
#TODO: Check if piece can be placed without intersecting another placed piece
return True
# Input: piece - PuzzlePiece object
# Insert piece into the next available position on the board and update state
def place(self, piece):
# TODO: Bug in this function. Pieces not being placed correctly.
position = self.__next()
if self.fits(piece):
for i in range(position[0], position[0] + piece.w ):
for j in range(position[1], position[1] + piece.l):
if((( piece.w + position[0] ) > len( self.state )) or (( piece.l + position[1] )> len( self.state[0] ))):
return
if(self.state[i][j]== 0):
#self.used_piece.append(piece)
self.state[i][j] = piece.id
else:
continue
return position
def check(self, piece):
position = self.__next()
if(position[0] + piece.w > self.w or position[1] + piece.l > self.l):
return False
return True
# Returns whether the board has been filledwith pieces
def completed(self):
return True if not self.__next() else False
def copy(self):
copied = PuzzleBoard(self.l, self.w)
copied.state = copy.deepcopy(self.state)
return copied
class PuzzlePiece():
def __init__(self, pid, length, width):
self.id = pid
self.l = length
self.w = width
itfits = False
def rotate(self):
#TODO: Bug in this function. Pieces are not rotating correctly
temp = self.l
self.l = self.w
self.w = temp
def orientation(self):
return "H" if self.w >= self.l else "V"
def __str__(self):
return f"ID: {self.id}, LENGTH: {self.l}, WIDTH: {self.w}, ROTATED: {self.rotated}"
def parse_input(filepath) :
#TODO: Bug in this function. Error raised when called
parsed = {'board' : {}, 'pieces' : {}}
with open(filepath, 'r') as f:
file_contents = f.read().strip().split("\n")
board_length, board_width = file_contents[0].strip().split(",")
parsed['board']['length'] = int(board_length)
parsed['board']['width'] = int(board_width)
for i in range(1, len(file_contents)):
#FIX: the issue was fix
pid, l, w = file_contents[i].strip().split(",")
pid, l, w = int(pid), int(l), int(w)
parsed['pieces'][pid] = {}
parsed['pieces'][pid]['length'] = l
parsed['pieces'][pid]['width'] = w
return parsed
def helper(board, piece):
unused = []
#for piece in pieces:
if board.fits(piece):
position = board.place(piece)
board.used_piece.append((piece, position))
return board
def solve(board, remaining, used_pieces=[]):
# TODO: Implement a solution for a variable amount of pieces and puzzle board size.
# HINT: Recursion might help.7
poss = queue.Queue()
poss.put(board)
currboard = PuzzleBoard(len(board.state), len(board.state[0]))
while not currboard.completed():
currboard = poss.get()
#print(currboard.state)
for piece in remaining:
fakeboard = copy.deepcopy(currboard)
if(not (piece.id in np.array(fakeboard.state))):
#if( fakeboard.check(piece)):
poss.put(helper(fakeboard, piece))
print("Suff done")
return currboard
'''if(len(remaining) != 0):
board, used_pieces, unused_pieces = helper(board, remaining, used_pieces)
if board.completed():
return board, used_pieces
for i in board.state:
print(i)
print("\n \n")
return solve(board, unused_pieces, used_pieces)
return board'''
def main():
#TODO: Bug in this function. Positions are not correct after solution is found.
parser = argparse.ArgumentParser()
parser.add_argument('input')
args = parser.parse_args()
parsed = parse_input(args.input)
board = PuzzleBoard(parsed['board']['length'], parsed['board']['width'])
pieces = []
for k, v in parsed['pieces'].items():
pieces.append(PuzzlePiece(k, v['length'], v['width']))
solved = solve(board, pieces)
if not solved:
print("No solution found for given input.")
else:
print("Solution found.")
board = solved
for u, position in solved.used_piece:
print(f"Piece ID: {u.id}, Position:{position}, Orientation: {u.orientation()}")
if __name__ == "__main__":
main()

newList = list - self

I have an list of objects called character like that:
characters=[character(0,0,20,20,keys1),character(50,50,50,50,keys2),character(200,200,100,20,keys1)]
where character class is defined as:
class character():
def __init__(self,x,y,w,h,keys=keys1):
self.x = x
self.y = y
self.w = w
self.h = h
self.keys = keys
self.vel = 200 /math.sqrt(self.w*self.h)
self.crashed=False
# I need an list to use here in a function of my "character" class
# my list must have all the elements in my initial list but self
what I mean is like
characters[0].myFunction() must have a list of [characters[1],characters[2]]
which is characters =[character(50,50,50,50,keys2),character(200,200,100,20,keys1)]
you have to pass as an argument to your .myFunction the full list to know from which list to exclude/filter self:
class character():
def __init__(self,x,y,w,h,keys=keys1):
self.x = x
self.y = y
self.w = w
self.h = h
self.keys = keys
self.vel = 200 /math.sqrt(self.w*self.h)
self.crashed = False
def __repr__(self):
return 'charcter({}, {}, {}, {}, {})'.format(
self.x, self.y, self.w, self.h, self.keys)
def myFunction(self, characters):
return [e for e in characters if e != self]
print(characters[0].myFunction(characters))
output:
[charcter(50, 50, 50, 50, keys2), charcter(200, 200, 100, 20, keys1)]
I Understand what you want but there are a few issues with your code that I decided to fix for you i guess. The first is to remember just for your sake that when making classes make the first letter upper case. Next your key creation does not make any sense because keys1 doesnt exsist. I assumed it to be a string in my example. Next I created an str function for your class so it can output the characters somehow so its not only the memory address. Next I created the function you want but it will be in the format
x = myFunc(characters[0], characters)
and on top of that if you print x it will give you a list of memory addresses and therefore you must loop through and print each character specifically to see which one it is. Here is the final code.
import math
class Character():
def __init__(self,x,y,w,h,keys="keys1"):
self.x = x
self.y = y
self.w = w
self.h = h
self.keys = keys
self.vel = 200 /math.sqrt(self.w*self.h)
self.crashed=False
# I need an list to use here in a function of my "character" class
# my list must have all the elements in my initial list but self
def __str__(self):
return "Character x:{}, y:{}, w:{}, h:{}, key:{}".format(self.x, self.y, self.w, self.h, self.keys)
def myFunc(chars, characters):
characters_copy = characters[:]
if chars in characters_copy:
ind = characters_copy.index(chars)
characters_copy.pop(ind)
return characters_copy
characters = [Character(0,0,20,20), Character(50,50,50,50), Character(200,200,100,20)]
x = myFunc(characters[0], characters)
for i in x:
print(i)
I believe this is somewhat what you wanted, I added the changes so its easier for me and you. But you should be able to work with this.

Python curses: addstr() from file prints blanks for the remainder of the line

I'm writing a very simple farming game in Python using curses. At this point I have been successful in allowing the player (just a "#" character) to move around within a window.
I have a few files with ascii-art that I print to the window as things to populate the world in which the player can move around. For example, I have a file, named "house", that contains:
_ . ^ . _
/____.____\
| |
| ## _ ## |
|_""_H_""_|
I have a Thing class as follows:
class Thing(object):
def __init__(self, Xstart, Ystart, looksLike, list, window):
self.Xstart = Xstart
self.Ystart = Ystart
self.X = Xstart
self.Y = Ystart
self.looksLike = looksLike
self.boundries = []
self.find_boundries()
list.append(self)
self.draw(window)
def find_boundries(self):
file = open(self.looksLike).readlines()
for line in file:
for char in line:
if char == '\n':
pass
elif char == ' ': # skip all whitespace
self.X += 1
else:
self.boundries.append([self.X, self.Y])
self.X += 1
self.Y += 1
self.X = self.Xstart
self.X = self.Xstart # reset x & y to starting coordinates
self.Y = self.Ystart
def draw(self, window):
#file = open(self.looksLike).readlines()
#for line in file:
# window.addstr(self.Y, self.X, line)
# self.Y += 1
#self.Y = self.Ystart
file = open(self.looksLike).read()
for char in file:
window.addch(self.Y, self.X, char)
if char == '\n':
self.Y += 1
self.X = self.Xstart
else:
self.X += 1
self.X = self.Xstart
self.Y = self.Ystart
Thus the constructor for my Thing class takes a filename as an argument (looksLike) and the draw method opens the file, reads it, and prints its contents to the window. I can, then, create a house object, pass my "house" file as an argument, and my ascii house will be printed to the window.
The problem is that once the object is printed to the window, when I move the player to the right of the printed object the player disappears. Above, below, and to the left of the printed object, however, the player stays in view. For example,
_ . ^ . _
/____.____\
| |
| ## _ ## |
|_""_H_""_|
#
In this position the "#" character is visible, but if I move one space up, it disappears. If I continue moving the player up, the "#" will reappear after it moves beyond the topmost character of the house.
I assume this issue is due to the nature of both addstr() and addch() ( I have tried both) printing whitespace until the end of the window, but I have been unable to find any documentation on this.
I have considered creating a new window for each object printed, but this seems like it would get quite cumbersome when more than a few objects are printed to the window. Also, I hope to define borders around the objects printed to the screen that are shapes other than just squares or rectangles.
Is there anyway to print from a file to the window without the trailing whitespace and without creating a new window for each printed object?
I assume this issue is due to the nature of both addstr() and addch() ( I have tried both) printing whitespace until the end of the window,
Why do you assume that?
First, you never actually call addstr in the code you've shown us, so that can't be it.
As for addch, it very definitely should not do that—as you could see by just, e.g., drawing from right to left. Or by running this trivial test code:
# usual curses setup
stdscr.addch(10, 10, 'a')
stdscr.addch(10, 9, 'b')
If you're not seeing the a in that test program, there's something wrong with your terminal. But if you are, then your assumption is wrong, and it has nothing to do with addch.
Almost certainly the problem is that you actually have spaces in the house file. In curses, if you draw a character on top of another character, it replaces the old character, it doesn't try to merge them or overstrike them or anything like that. (Which is good, because most consoles don't have any way to do any such thing…)
If the new character is a space, it just replaces the old character with a space. Exactly like you're seeing.
So, the fix is to remove all those spaces on the end of each line, right? Well, you could do that. Or you could just rstrip() each line. (You don't need the \n; you can tell that you've gotten to the end of a line by the fact that you've finished iterating over the whole line, right? Well, you can in your file = open(…).readlines() code, or in code that didn't bother with the readlines() and just looped over the file itself; you can't in your different file = open(…).read() code, but I don't know why you're doing it differently there in the first place.)
Or, since your find_boundries function very carefully skips over spaces, maybe you wanted to do the same thing in draw but just forgot to? If so, just write the code you intended to skip over the spaces.
But there's a much simpler solution to the whole problem: Just draw the # after the house instead of before it, and this won't even be an issue in the first place. Of course that means that if the player is in the same place as the house, he'll show up "outside" of it rather than being hidden "inside"—but you already appear to have code to prevent that from ever happening, so it shouldn't matter who it would look like if it happened.
Onfortunately I have never used curses, and I cannot see your player class.
Nevertheless maybe this snippet might give you some ideas (press 'x' to exit game) (use WASD to move the player) (requires ANSI-enabled console:
#! /usr/bin/python3
class _GetchUnix:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
getch = _GetchUnix ()
house = ''' _ . ^ . _
/____.____\\
| |
| ## _ ## |
|_""_H_""_|'''
class Screen:
def __init__ (self, width, height, background):
self.width = width
self.height = height
self.bg = '\x1b[{}m'.format (40 + background)
self.clear = '\x1b[0m'
self.objects = []
def __iadd__ (self, obj):
self.objects.append (obj)
obj.screen = self
return self
def render (self):
print ('\x1b[1;1H', end = '')
for y in range (self.height):
for x in range (self.width):
print (self.bg + ' ' + self.clear, end = '')
print ()
for obj in self.objects: obj.render ()
print ('\x1b[{};1H'.format (self.height) )
class Object:
def __init__ (self, graphics, foreground, background, x, y):
self.graphics = graphics.split ('\n')
self.fg = '\x1b[{}m'.format (30 + foreground)
self.bg = '\x1b[{}m'.format (40 + background)
self.clear = '\x1b[0m'
self.x = x
self.y = y
def render (self):
for y, line in enumerate (self.graphics):
print ('\x1b[{};{}H'.format (self.y + y, self.x), end = '')
print (self.fg + self.bg + line + self.clear)
def collide (self, x, y):
if y < self.y: return False
if x < self.x: return False
if y > self.y + len (self.graphics) - 1: return False
if x > self.x + len (self.graphics [y - self.y] ): return False
return True
def move (self, dx, dy):
nx, ny = self.x + dx, self.y + dy
if ny < 1: return
if ny > self.screen.height: return
if nx < 1: return
if nx > self.screen.width: return
for obj in self.screen.objects:
if obj == self: continue
if obj.collide (nx, ny): return
self.x, self.y = nx, ny
house = Object (house, 0, 7, 6, 3)
player = Object ('#', 1, 3, 10, 10)
s = Screen (40, 20, 3)
s += house
s += player
while True:
c = getch ()
if c == 'x': break
if c == 'w': player.move (0, -1)
if c == 's': player.move (0, 1)
if c == 'a': player.move (-1, 0)
if c == 'd': player.move (1, 0)
s.render ()
Here a screen shot:

Obtaining values from dictionary in OOP: "AttributeError"

I am trying to get values from my dictionary VALUES. My program creates combination of possible positions and gets the last position. Then I want to get the value. Everything works well here except indicated .get_value method. When I execute this code I receive:
AttributeError: 'Combination' object has no attribute 'get_value'
Theoretically it should be easy but I am new to OOP and I don't see what is wrong here.
X = ['A','B','C']
Y = ['1','2','3']
VALUES = {'A':10, 'B': 50, 'C':-20}
class Combination:
def __init__(self,x,y):
if (x in X) and (y in Y):
self.x = x
self.y = y
else:
print "WRONG!!"
def __repr__ (self):
return self.x+self.y
def get_x(self):
return self.x
def get_y(self):
return self.y
class Position:
def __init__(self):
self.xy = []
for i in X:
for j in Y:
self.xy.append(Combination(i,j))
def choose_last(self):
return self.xy.pop()
def __str__(self):
return "List contains: " + str(self.xy)
class Operation1:
def __init__(self):
self.operation1 = []
def __str__(self):
s = str(self.operation1)
return s
def get_value(self):
V = VALUES.get(self)
return V
pos = Position()
print pos
last_item = pos.choose_last()
print "Last item:", last_item, pos
last_value = last_item.get_value() # <---- Here is a problem
How can I obtain value of my position? Value is determined by the X value - this is A,B or C. In the dictionary I have a numeral value for the letter.
You are appending objects of Combination into xy of Position. When you say choose_last, it will return the last Combination object inserted into xy. And you are trying to invoke get_value method on a Combination object, which doesnt have that method. Thats why you are getting that error.
Always use new style classes.

Why does the print statement at the bottom of my main method not print anything?

I'm working on the MIT open courseware for CS-600 and I can't figure out why the last print statement isn't printing anything. Here's the code I wrote:
#!/usr/bin/env python
# encoding: utf-8
# 6.00 Problem Set 9
#
# Name:
# Collaborators:
# Time:
from string import *
class Shape(object):
def area(self):
raise AttributeException("Subclasses should override this method.")
class Square(Shape):
def __init__(self, h):
"""
h: length of side of the square
"""
self.side = float(h)
def area(self):
"""
Returns area of the square
"""
return self.side**2
def __str__(self):
return 'Square with side ' + str(self.side)
def __eq__(self, other):
"""
Two squares are equal if they have the same dimension.
other: object to check for equality
"""
return type(other) == Square and self.side == other.side
class Circle(Shape):
def __init__(self, radius):
"""
radius: radius of the circle
"""
self.radius = float(radius)
def area(self):
"""
Returns approximate area of the circle
"""
return 3.14159*(self.radius**2)
def __str__(self):
return 'Circle with radius ' + str(self.radius)
def __eq__(self, other):
"""
Two circles are equal if they have the same radius.
other: object to check for equality
"""
return type(other) == Circle and self.radius == other.radius
#
# Problem 1: Create the Triangle class
#
## TO DO: Implement the `Triangle` class, which also extends `Shape`.
class Triangle(Shape):
def __init__(self, base, height):
self.base = float(base)
self.height = float(height)
def area(self):
return self.base*self.height/2
def __str__(self):
return 'Triangle with base ' + str(self.base) + 'and height ' + str(self.height)
def __eq__(self, other):
return type(other) == Triangle and self.base == other.base and self.height == other.height
#
# Problem 2: Create the ShapeSet class
#
## TO DO: Fill in the following code skeleton according to the
## specifications.
class ShapeSet(object):
def __init__(self):
"""
Initialize any needed variables
"""
self.allCircles = []
self.allSquares = []
self.allTriangles = []
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
self.place = None
def addShape(self, sh):
"""
Add shape sh to the set; no two shapes in the set may be
identical
sh: shape to be added
"""
if not isinstance(sh, Shape): raise TypeError('not a shape')
if isinstance(sh, Square):
for sq in self.allSquares:
if sh == sq:
raise ValueError('shape already in the set')
self.allSquares.append(sh)
if isinstance(sh, Triangle):
for tri in self.allTriangles:
if sh == tri:
raise ValueError('shape already in the set')
self.allTriangles.append(sh)
if isinstance(sh, Circle):
for circ in self.allCircles:
if sh == circ:
raise ValueError('shape already in the set')
self.allCircles.append(sh)
def __iter__(self):
"""
Return an iterator that allows you to iterate over the set of
shapes, one shape at a time
"""
self.place = 0
return self
def next(self):
if self.place >= len(self.allShapes):
raise StopIteration
self.place += 1
return self.allShapes[self.place - 1]
def __str__(self):
"""
Return the string representation for a set, which consists of
the string representation of each shape, categorized by type
(circles, then squares, then triangles)
"""
shapeList = ""
for item in self.allShapes:
shapeList += item.get__str__ + "br/"
return shapeList
#
# Problem 3: Find the largest shapes in a ShapeSet
#
def findLargest(shapes):
"""
Returns a tuple containing the elements of ShapeSet with the
largest area.
shapes: ShapeSet
"""
## TO DO
#
# Problem 4: Read shapes from a file into a ShapeSet
#
def readShapesFromFile(filename):
"""
Retrieves shape information from the given file.
Creates and returns a ShapeSet with the shapes found.
filename: string
"""
## TO DO
def main():
sq1 = Square(4.0)
sq2 = Square(5.0)
sq3 = Square(3.0)
circ1 = Circle(3.0)
circ2 = Circle(3.2)
tri1 = Triangle(3.0, 4.0)
tri2 = Triangle(4.0, 3.0)
tri3 = Triangle(1.0, 1.0)
thisSet = ShapeSet()
thisSet.addShape(sq1)
thisSet.addShape(sq2)
thisSet.addShape(sq3)
thisSet.addShape(circ1)
thisSet.addShape(circ2)
thisSet.addShape(tri1)
thisSet.addShape(tri2)
thisSet.addShape(tri3)
print thisSet
if __name__ == '__main__':
main()
This line:
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
doesn't do what you think it does. It sets allShapes to an empty list, and then as you add shapes later, nothing updates allShapes.
Then your __str__ function just loops over allShapes, which is still empty, so your __str__ returns an empty string.
This line makes allShapes an empty list:
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
If you modify allCircles, that doesn't affect allShapes. I would personally eliminate allShapes, and in the str method, add them at the last possible second:
for item in self.allCircles + self.allSquares + self.allTriangles:
The problem is here:
self.allShapes = self.allCircles + self.allSquares + self.allTriangles
When you concatenate lists like this, the result is a copy of the component lists. So when those lists are changed later, the concatenated list isn't changed. In this case, self.allCircles, etc. are all empty. So self.allShapes is an empty list too; the for loop in ShapeSet.__str__ doesn't append anything to ShapeList, and so the result is an empty string.
One simple way to fix this would be to make allShapes a method that you call, and that returns a new concatenation of self.allCircles... etc. each time it's called. That way, allShapes is always up-to-date.
If this is your actual code, then it must be because of
item.get__str__
which should raise an exception.
Edit: as others have noted, this isn't the actual problem, but I leave this here as a hint for further progress. Mind you, it's considered bad style ("unpythonic") to call x.__str__() directly, as you probably intended. Call str(x) instead, even in the implementation of __str__.
You assign allShapes to be the value of self.allCircles + self.allSquares + self.allTriangles at the start in your init method (when the other lists are empty).
It's value is then never changed, so it remains empty.
You need this in addShape:
self.allShapes.append(sh)

Categories