Python class condition: getting an [x,x] output from an x input? - python

I'm playing around with some interval calculations in Python. This is an excerpt of what I've written. I want to include degenerate intervals i.e. the interval for the real number 1 is [1,1].
So when I type Interval(1), I want [1,1] returned. But I've defined my interval class in terms of two parameters.
I can't make a subclass - it would still expect two parameters. Can anyone point me in the right direction please? Could I extend the __contains__ in some sense?
TLDR: How can I get an [x,x] output from an x input?
from numpy import *
import numpy as np
from pylab import *
class Interval:
def __init__(self, LeftEndpoint, RightEndpoint):
self.min = LeftEndpoint
self.max = RightEndpoint
if LeftEndpoint > RightEndpoint:
raise ValueError('LeftEndpoint must not be greater than RightEndpoint')
def __repr__(self): #TASK 3
return '[{},{}]'.format(self.min,self.max)
def __contains__(self, num):
if num < self.min:
raise Exception('The number is not in the given interval')
if num > self.max:
raise Exception('The number is not in the given interval')
p = Interval(1,2)
print(p) #returns [1,2]

Just give RightEndpoint a default value, like None; if it is still None in the function, you know no value was assigned to it and thus can be set to the same value as LeftEndpoint:
def __init__(self, LeftEndpoint, RightEndpoint=None):
if RightEndpoint is None:
RightEndpoint = LeftEndpoint
Note: if you follow the Python styleguide, PEP 8, parameter and local variable names should be all lowercase_with_underscores. And the __contains__ method should really return True or False, not raise an exception:
class Interval:
def __init__(self, left, right=None):
if right is None:
right = left
if left > right:
raise ValueError(
'left must not be greater than right')
self.left = left
self.right = right
def __repr__(self):
return f'[{self.left}, {self.right}]'
def __contains__(self, num):
return self.left <= num <= self.right
Note that the __contains__ method can now be expressed as a single test; either num is in the (inclusive) range of left to right, or it is not.
Demo:
>>> Interval(1, 2)
[1, 2]
>>> Interval(1)
[1, 1]
>>> 11 in Interval(42, 81)
False
>>> 55 in Interval(42, 81)
True

You can use *args to pass in a variable number of arguments, then just assign RightEndpoint dynamically:
def __init__(self, *args):
self.min = args[0]
if len(args) < 2:
RightEndpoint = args[0]
elif len(args) == 2:
RightEndpoint = args[1]
else:
# decide what to do with more than 2 inputs
self.max = RightEndpoint
#...
p = Interval(1,2)
print(p) #returns [1,2]
p1 = Interval(1)
print(p1) #returns [1,1]
(Alternately you can use **kwargs if you want to use keyword arguments instead.)

Related

Rewriting recursive python function without helper causes it to stop working

Basically a tried to rewrite a recursive function in Python without using a helper function. The code is almost identical, but I'm getting lots of unexpected results. Can anyone tell what I'm missing?
Here is the working code:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def reconstructBst(preOrderTraversalValues):
root_index = [0]
return reconstructFromRange(float("-inf"), float("inf"), preOrderTraversalValues, root_index)
def reconstructFromRange(low, high, values, root_index):
if root_index[0] == len(values):
return None
root_value = values[root_index[0]]
if root_value < low or root_value >= high:
return None
root_index[0] += 1
left_subtree = reconstructFromRange(low, root_value, values, root_index)
right_subtree = reconstructFromRange(root_value, high, values, root_index)
return BST(root_value, left_subtree, right_subtree)
Here is the non-working rewrite:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def reconstructBst(preOrderTraversalValues, low=float("-inf"), high=float("inf"), root_index=[0]):
if root_index[0] == len(preOrderTraversalValues):
return None
root_value = preOrderTraversalValues[root_index[0]]
if root_value < low or root_value >= high:
return None
root_index[0] += 1
left_subtree = reconstructBst(preOrderTraversalValues, low, root_value, root_index)
right_subtree = reconstructBst(preOrderTraversalValues, root_value, high, root_index)
return BST(root_value, left_subtree, right_subtree)
Here is the error that comes up for most of the test cases:
list index out of range
Traceback (most recent call last):
File "/tester/json_wrapper.py", line 30, in getActual
result = program.reconstructBst(preOrderTraversalValues)
File "/tester/program.py", line 13, in reconstructBst
root_value = preOrderTraversalValues[root_index[0]]
IndexError: list index out of range
In the original code, whenever reconstructBst runs, it creates a new root_index = [0] and gives it to reconstructFromRange, which mutates root_index in the line root_index[0] += 1.
In your edit, you moved the creation root_index=[0] to a default argument of reconstructBst. It is not created when reconstructBst runs, but at its DEFINITION; think of it as an object that belongs to the function itself. Now every time reconstructBst runs, it changes its root_index value.
I'll bet that root_index was no longer [0] after the first run, and that caused a problem because preOrderTraversalValues only starts with 1 value and could only be indexed by preOrderTraversalValues[0].
The easy fix is to use a dummy immutable value to specify when to create a mutable object:
def reconstructBst(preOrderTraversalValues, low=float("-inf"), high=float("inf"), root_index=None):
if root_index == None:
root_index = [0]
...

AttributeError: 'int' object has no attribute 'map' when trying to write a recursive algorithm for a depth limited search

Trying to write a recursive algorithm for a depth limited search for a 6-puzzle game but for some reason i keep getting the above error and cannot understand why.
This is my code for the recursve depth limited search:
def rec_dls(node, limit):
global cutoff_occurred
cutoff = 0
failure = -1
if goal_state == node.state:
return node
elif limit == 0:
return cutoff # cutoff
else:
cutoff_occurred = False
actions = moves(node.state) # all possibles children of this state
for action in actions:
child = State(action, node, node.depth+1, node.cost+1)
result = rec_dls(child, limit - 1)
if result == cutoff:
cutoff_occurred = True
elif result != failure:
return result
if cutoff_occurred:
return cutoff
else:
return failure
def dls(limit):
node = State(initial_state, None, 0, 0)
return rec_dls(node, limit)
and also the State class:
class State:
def __init__(self, state, parent, depth, cost):
self.state = state
self.parent = parent
self.depth = depth
self.cost = cost
if self.state:
self.map = ''.join(str(e) for e in self.state)
def __eq__(self, other):
return self.map == other.map
def __lt__(self, other):
return self.map < other.map
this is the error i have in more details:
for reference, i am basing the logic of my work on this (from "Artificial Intelligence, A Modern Approach"):
The problem isn't when rec_dls returns an int. It's when it returns one of your State objects.
Consider the following lines of code:
result = rec_dls(child, limit - 1)
if result == cutoff:
# ...
Suppose rec_dls here returns one of your State objects. You then compare your State object against cutoff, which contains the int value 0, and because you override __eq__ in your State class, this comparison causes State.__eq__ to be called with other set to 0.
Being an int, 0 doesn't have a map attribute, hence your error.
Perhaps you want to include in your __eq__ method a check that other is another State object:
def __eq__(self, other):
return isinstance(other, State) and self.map == other.map

Objects passing objects

I'm new to python and am currently trying to use an old module to output graphs. The code below is a excerpt from the module that uses rpy to design
standard celeration charts (don't look it up).
I'm having trouble understanding how the class Element and class Vector work together.
I've been trying to pass the a element object to the vector get_elements but I'm not sure if that's what I should be doing.
Any help would be appreciated. Thanks!
class Element(object):
"""Base class for Chartshare vector elements."""
def __init__(self, offset=0, value=0):
self.offset=offset
self.value=value
self.text=''
def setText(self, value):
self.value=value
def getText(self):
return self.value
text = property(getText, setText)
class Vector(object):
"""Base class for Chartshare Vectors."""
def __init__(self, name='', color='black', linetype='o', symbol=1, clutter=0, start=0, end=140, continuous=False, debug=False):
self.name=name
self.color=color
self.linetype=linetype
self.symbol=symbol
self.start=start
self.end=end
self.elements={}
self.debug=debug
self.continuous=continuous
if not self.continuous:
for i in range(self.start, self.end+1):
self.elements[i]='NaN'
def getSymbol(self):
return self._symbol
def setSymbol(self, value):
if (type(value) == int):
if (value >= 0) and (value <= 18):
self._symbol = value
else:
raise SymbolOutOfRange, "Symbol should be an integer between 0 and 18."
elif (type(value) == str):
try:
self._symbol = value[0]
except IndexError:
self._symbol=1
else:
self._symbol = 1
symbol = property(getSymbol, setSymbol)
def getLinetype(self):
return self._linetype
def setLinetype(self, value):
if (value == 'p') or (value == 'o') or (value == 'l'):
self._linetype = value
else:
raise InvalidLinetype, "Line type should be 'o', 'p', or 'l'"
linetype = property(getLinetype, setLinetype)
def get_elements(self):
"""Returns a list with the elements of a Vector."""
retval = []
for i in range(self.start, self.end+1):
if (not self.continuous):
retval.append(self.elements[i])
else:
if (self.elements[i] != 'NaN'):
retval.append(self.elements[i])
return retval
def get_offsets(self):
"""Returns a list of the offsets of a Vector."""
retval = []
for i in range(self.start, self.end+1):
if (not self.continuous):
retval.append(i)
else:
if (self.elements[i] == 'NaN'):
retval.append(i)
return retval
def to_xml(self, container=False):
"""Returns an xml representation of the Vector."""
if (container == False):
container = StringIO.StringIO()
xml = XMLGenerator(container)
attrs = {}
attrs[u'name'] = u"%s" % self.name
attrs[u'symbol'] = u"%s" % self.symbol
attrs[u'linetype'] = u"%s" % self.linetype
attrs[u'color'] = u"%s" % self.color
xml.startElement(u'vector', attrs)
for i in range(self.start, self.end+1):
if (self.elements[i] != 'NaN'):
attrs.clear()
attrs[u'offset'] = u"%s" % i
xml.startElement(u'element', attrs)
xml.characters(u"%s" % self.elements[i])
xml.endElement(u'element')
xml.endElement(u'vector')
def render(self):
"""Plots the current vector."""
if (self.debug):
print "Rendering Vector: %s" % self.name
print self.elements
r.points(x=range(self.start, self.end+1),
y=self.elements,
col=self.color,
type=self.linetype,
pch=self.symbol)
if (self.debug):
print "Finished rendering Vector: %s" % self.name
Vector's get_elements() doesn't take any arguments. Well, technically it does. It takes self. self is syntactic sugar that lets you do this:
vec = Vector()
vec.get_elements()
It's equivalent to this:
vec = Vector()
Vector.get_elements(vec)
Since get_elements() doesn't take any arguments, you can't pass a to it. Skimming the code, I don't see a set_elements() analog. This means you'll have to modify the vector's element's dictionary directly.
vec = Vector()
vec.elements[a] = ...
print(vec.get_elements()) # >>> [a,...]
As I can see, there is no place in this code where you are assigning self.elements with any input from a function. You are only initialising it or obtaining values
Also note that the .get_elements() function doesn't have any arguments (only self, that is the object where you are calling it in), so of course it won't work.
Unless you can do something such as the following, we would need more code to understand how to manipulate and connect these two objects.
element_obj = Element()
vector_obj = Vector()
position = 4
vector_obj.elements[4] = element_obj
I got to this answer with the following: as I can see, the elements property in the Vector class is a dictonary, that when you call vector_obj.get_elements() is casted to an array using the start and end parameters as delimiters.
Unless there is something else missing, this would be the only way I could think out of adding the an element into a vector object. Otheriwse, we would need some more code or context to understand how these classes behave with each other!
Hope it helps!

Function to determine whether tree is a valid BST?

I have to determine whether given a list representing a tree, whether the tree is a valid BST (this question is taken from leetcode). I have seen other posts on this but I was wondering if someone could help me with my approach, since it is clearly not right. For example, for the tree [1,2,3] where 1 is the root, 2 is the left child, and 3 is the right child, my code returns true. Hopefully it only requires small changes, but it might be that the entire function's approach is incorrect.
Here is my code:
def isValidBST(self, root):
if (root == None):
return True
if (root.left == None or root.left.val < root.val):
return self.isValidBST(root.left)
if (root.right == None or root.right.val > root.val):
return self.isValidBST(root.right)
return False
Secondly, I have seen approaches with a helper function that takes in a min/max value, but that confuses me. If anyone would also like to explain why that approach is a good/better one, that would be greatly appreciated!
I'd make a min_max method for Nodes that finds the min and max values of the tree rooted at that Node. Do sanity checking while finding those, and then isValidBST can just catch the exception
def max_min(self):
'''
Returns maximum and minimum values of the keys of the tree rooted at self.
Throws an exception if the results are not correct for a BST
'''
l_max, l_min = self.left.max_min() if self.left else (self.val, self.val)
if l_max > self.val:
raise ValueError('Not a BST')
r_max, r_min = self.right.max_min() if self.right else (self.val, self.val)
if r_min < self.val:
raise ValueError('Not a BST')
return l_min, r_max
def isValidBST(self):
try:
if self.max_min():
return True
except ValueError:
return False
Here is one way to implement the validity check:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def isValidBST(self):
'''
Simultaneously check for validity and set our own min and max values.
'''
self.min = self.max = self.value
if self.left:
if not self.left.isValidBST():
return False
if self.left.max >= self.value:
return False
self.min = self.left.min
if self.right:
if not self.right.isValidBST():
return False
if self.right.min < self.value:
return False
self.max = self.right.max
return True
assert BST(2, BST(1), BST(3)).isValidBST()
case = BST(2, BST(1, None, BST(3)))
assert case.left.isValidBST()
assert not case.isValidBST()

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