(Python 2.7) Access variable from class with accessor/mutator - python

(Python 2.7) I'm trying to access the vertices variable from the SierpinskiTriangle class and use it in the second bit of code listed but it shows
TypeError: 'property' object is not iterable
I can only assume it is due to the accessors/mutators
Base code:
class Fractal(object):
# the constructor
def __init__(self, dimensions):
# the canvas dimensions
self.dimensions = dimensions
# the default number of points to plot is 50,000
self.num_points = 50000
# the default distance ratio is 0.5 (halfway)
self.r = 0.5
# accessors and mutators
#property
def vertices(self):
return self._vertices
#vertices.setter
def vertices(self, v):
self._vertices = v
class SierpinskiTriangle(Fractal):
# the constructor
def __init__(self, canvas):
# call the constructor in the superclass
Fractal.__init__(self, canvas)
# define the vertices based on the fractal size
v1 = Point(self.dimensions["mid_x"], self.dimensions["min_y"])
v2 = Point(self.dimensions["min_x"], self.dimensions["max_y"])
v3 = Point(self.dimensions["max_x"], self.dimensions["max_y"])
self.vertices = [ v1, v2, v3 ]
Code to get vertices in:
class ChaosGame(Canvas):
vertex_radius = 2
vertex_color = "red"
point_radius = 0
point_color = "black"
def __init__(self, master):
Canvas.__init__(self, master, bg = "white")
self.pack(fill = BOTH, expand = 1)
# a function that takes a string that represents the fractal to create
def make(self, f):
if f == "SierpinskiTriangle":
vertices = SierpinskiTriangle.vertices
if f == "SierpinskiCarpet":
vertices = []
if f == "Pentagon":
vertices = []
if f == "Hexagon":
vertices = []
if f == "Octagon":
vertices = []
print vertices
for point in vertices:
self.plot_point(self, point, ChaosGame.vertex_color, ChaosGame.vertex_radius)

This is because you are accessing the class instead of an object of that type.
Let's try it on a minimal example:
class Container:
def __init__(self):
self._content = range(10)
#property
def content(self):
return self._content
#content.setter
def set_content(self, c):
self._content = c
This works:
c = Container()
for number in c.content:
print(number)
(prints out numbers from 0 to 9).
But this fails:
for number in Container.content:
print(number)
with the error
TypeError Traceback (most recent call last)
<ipython-input-27-f1df89781355> in <module>()
1 # This doesn't:
----> 2 for number in Container.content:
3 print(number)
TypeError: 'property' object is not iterable
Besides of the problems with the properties, you didn't initialize an object, so the __init__ function of the class was never called and Container._content was not initialized.
In fact, you would get a similar problem if you had just used
class Container:
def __init__(self):
self.container = range(10)
(only that it would be an attribute error in this case).
Final note: This
for number in Container().content: # note the '()'!!
print(number)
works again, because we create a container object on the fly.

Related

Checking if contained object has changed

I am working on creating a module with a class that acts as a container for a list of another created class. Is there a way for the container class to be able to tell if any of the objects it contains has changed?
Here is an example:
class Part:
def __init__(self, size):
self.part_size = size
class Assembly:
def __init__(self, *parts):
self.parts = list(parts) # `parts` are all Part() objects
self.update()
def update(self):
self.assy_size = 0
for each in self.parts:
self.assy_size += each.part_size
def __getitem__(self, key):
return self.parts[key]
This is what I get if I try to change any of the Part properties in the Assembly:
>>>x = Part(1)
>>>y = Part(1)
>>>z = Part(1)
>>>u = Assembly(x, y, z)
>>>u.assy_size
3
>>>u[0].part_size = 4
>>>u.assy_size
3
I know that I can create additional methods that will call the update method if I replace, delete, or add Part objects to the Assembly, but is there any way to have the Assembly notified if any of the contained Part properties have changed?
The answer is in your question. Use a property.
class Part:
_size = 0
assembly = None
#property
def part_size(self):
return self._size
#part_size.setter
def part_size(self, value):
self._size = value
if self.assembly: # only notify if an Assembly is set
self.assembly.update()
def set_assembly(self, assembly):
self.assembly = assembly
def __init__(self, size):
self.part_size = size
class Assembly:
def __init__(self, *parts):
self.parts = list(parts) # `parts` are all Part() objects
for part in self.parts:
part.set_assembly(self) # reference to self needed to notify changes
self.update()
def update(self):
self.assy_size = 0
for each in self.parts:
self.assy_size += each.part_size
In this version of Assembly the constructor sets a reference on the Part to itself. This way it can update the assembly when the part_size changes. Use it as the example in your question.
>>>x = Part(1)
>>>y = Part(1)
>>>z = Part(1)
>>>u = Assembly(x, y, z)
>>>u.assy_size
3
>>>u[0].part_size = 4
>>>u.assy_size
6
If update isn't an expensive operation (in your example it isn't, but maybe in reality you have thousands of parts), you could calculate the size ad-hoc using a property:
class Assembly:
def __init__(self, *parts):
self.parts = list(parts)
#property
def assy_size(self):
result = 0
for each in self.parts:
result += each.part_size
return result
which can be accessed the same way: assembly.assy_size.
The calculation can also be simplified:
#property
def assy_size(self):
return sum(part.part_size for part in self.parts)

Python: Dijkstra' algorithm

need a help with Dijkstra. I found a lot of codes on the internet, but I can't use any of them, because I'm not given a graph, but just lists of Vertexes & Edges into createGraph function. It's a homework and I gotta have some attributes in classes.
This is what I have:
class Vertex:
def __init__(self, id, name):
self.id = id
self.name = name
self.minDistance = float('inf')
self.previousVertex = None
self.edges = []
self.visited = False
class Edge:
def __init__(self, source, target, weight):
self.source = source
self.target = target
self.weight = weight
class Dijkstra:
def __init__(self):
self.vertexes = []
self.result = 0
def createGraph(self, vertexes, edgesToVertexes):
for i in range(len(vertexes)):
self.vertexes.append(vertexes[i])
for j in range(len(edgesToVertexes)):
if edgesToVertexes[j].source == vertexes[i].id:
vertexes[i].edges.append(edgesToVertexes[j])
def getVertexes(self):
return self.vertexes
def findMinID(self):
maxDistance = 1000000
curVertex = None
result = None
for i in range(len(self.vertexes)):
self.vertexes[i] = curVertex
if curVertex.visited is False and curVertex.minDistance < maxDistance:
curVertex = result
curVertex.minDistance = maxDistance
else:
pass
self.result = result
return
def computePath(self, sourceId):
start = None
end = None
road = None
while start is None:
if Vertex.id == sourceId:
start = Vertex
start.minDistance = 0
start.visited = True
for i in range(len(start.edges)):
start.edges[i].target = end
start.edges[i].weight = road
if road < end.minDistance:
end.minDistance = start.minDistance + road
end.previousVertex = start.id
else:
pass
self.findMinID()
self.computePath(self.result.id)
I'm still beginner so I tried to keep it simple, but it's not working as it raises error:
'type' object is not subscriptable
or:
AttributeError: type object 'Vertex' has no attribute 'id'
which makes absolutely no sense to me why.
I can use any help, thanks in advance!
When you put the line:
self.vertexes = Vertex
you are assigning the variable to the actual class. Probably what you wanted to do was make an empty list, as you append to it later:
self.vertexes = []
I would assume this is where the error comes from, as if you ever try to iterate over self.vertexes, you are iterating over the Vertex class, which is impossible and throws that error.
You also have later:
start = Vertex
Try initializing the start, like:
start = Vertex(sourceId, "vertex")
Also, the line before that you have
if Vertex.id == sourceId:
meaning that you might want to make the id variable in Vertex static:
class Vertex:
id = 0
def __init__(self, id, name):
self.id = id
id += 1
Some suggestions: class tutorial in python
Edit:
To find the vertex that has the id you want, use a filter:
start = None
for v in self.vertexes:
if v.id == sourceId:
start = Vertex(sourceId, v.name)
start.minDistance = 0
break

Python thinks object instance is list object

So I am implementing BFS on a Graph to detect all the cycles. I implemented the graph via an adjacency list. But when I run my code I get the following error
Traceback (most recent call last):
File "C:\Python27\Data Structures\Graph\bfstree.py", line 228, in <module>
main()
File "C:\Python27\Data Structures\Graph\bfstree.py", line 223, in main
traverse(g.getVertex(2))
File "C:\Python27\Data Structures\Graph\bfstree.py", line 168, in traverse
while (x.getPred()):
AttributeError: 'list' object has no attribute 'getPred'
So the problem occurs when I call the traverse() function.
Here is my main function
def main():
g = Graph()
for i in range(1,9):
g.addVertex(i)
g.addEdge(1,2)
g.addEdge(1,4)
g.addEdge(1,8)
g.addEdge(2,3)
g.addEdge(2,1)
g.addEdge(3,2)
g.addEdge(3,4)
g.addEdge(3,7)
g.addEdge(3,8)
g.addEdge(4,1)
g.addEdge(4,3)
g.addEdge(4,5)
g.addEdge(5,4)
g.addEdge(5,6)
g.addEdge(5,7)
g.addEdge(6,5)
g.addEdge(6,7)
g.addEdge(7,3)
g.addEdge(7,6)
g.addEdge(7,5)
g.addEdge(8,3)
g.addEdge(8,1)
for v in g:
for w in v.getConnections():
print("(%s,%s)"%(v.getId(),w.getId()))
print("\nDoing BFS...")
bfs_tree(g,g.getVertex(1))
a = g.getVertex(2)
print(type(a))
traverse(g.getVertex(2))
main()
Here is the traverse function:
def traverse(y):
x = y
while (x.getPred()):
print(x.getId())
x = x.getPred()
print(x.getId())
Here is the adjacency list implementation of the graph:
class Graph:
def __init__(self):
self.vertList = {} #this is the masterlist
self.numVertices = 0
def addVertex(self,key): #turn something into a Vertex object
self.numVertices = self.numVertices + 1
newVertex = Vertex(key)
self.vertList[key] = newVertex #maps vertex names to vertex objects
return newVertex
def getVertex(self,n):
if n in self.vertList:
return self.vertList[n] #returns the Vertex object
else:
return None
def __contains__(self,n):#tweak the built-in operator 'in'(containment check)
return n in self.vertList
def addEdge(self,f,t,cost = 0):
if f not in self.vertList: #if f is not a node in the graph
nv = self.addVertex(f)
if t not in self.vertList: #if t is not a node in the graph
nv = self.addVertex(t)
self.vertList[f].addNeighbor(self.vertList[t], cost)
def getVertices(self):
return self.vertList.keys()
def __iter__(self): # iterate over Vertex objects over the Graph
return iter(self.vertList.values())
class Vertex:
def __init__(self,key):
self.id = key
self.connectedTo={} #dictionary which contains all the other vertices it is connected to
self.pred = [] #for BFS tree / a list because we are dealing with cycles
self.color = "white" #for BFS tree
def addNeighbor(self,nbr,weight=0):
self.connectedTo[nbr] = weight #nbr is another Vertex object
def __str__(self):
#TODO: lookup how that for loop works
return str(self.id) + "connected to " + str([x.id for x in self.connectedTo])
def getConnections(self):
return self.connectedTo.keys()
def getId(self):
return self.id
def getWeight(self,nbr):
return self.connectedTo[nbr]
def getColor(self):
return self.color
def setColor(self,color):
self.color = color
def setPred(self,node):
self.pred.append(node)
def getPred(self):
if len(self.pred)>1:
return self.pred
elif len(self.pred) == 0:
return self.pred[0]
else:
return self.pred
Why is it saying that g.getVertex(2) is a list object? I am pretty sure that it's a Vertex object. I even printed out the type in the main function and it says it's an instance and not a list object.
You replace x with the result of x.getPred() here:
while (x.getPred()):
print(x.getId())
x = x.getPred()
x.getPred() returns self.pred:
def getPred(self):
if len(self.pred)>1:
return self.pred
elif len(self.pred) == 0:
return self.pred[0]
else:
return self.pred
(Note that for len(self.pred) == 0 you try to return self.pred[0], which will raise an IndexError exception).
self.pred is a list:
class Vertex:
def __init__(self,key):
# ...
self.pred = [] #for BFS tree / a list because we are dealing with cycles
So you replaced x with a list object, then loop back and call x.getPred() on that list object.
x = x.getPred() is the problem. The first check in the while loop is fine, but it breaks after x is updated the first time, then rechecked.
As implemented, getPred is returning self.pred (the only case where it returns a value from self.pred instead of the whole thing is broken; the length is 0, and you index, so it will raise IndexError). self.pred is a list.

Python accessing instance variable dynamically in method

I am trying to add a value to a instance list in python but want to access it dynamically from within a method.
I cannot use dictionaries as I am trying to speed up the sorting of separate lists (boxes) rather than one large list.
Can anybody show me the correct way to do the following?
class Boxes:
def __init__(self):
self.box1 = []
self.box2 = []
#....
self.box10 = []
def addToBox(self, value):
box = self.determineBoxToUse(value)
## box = 2
varname = "box", box ## box2
self.varname.insert(0, value)
def determineBoxToUse(self, value):
## for this example returns 2
return 2
def dumpBox(self):
print self.box2
Boxes = Boxes();
Boxes.addToBox("123.001234")
Boxes.dumpBox()
Error: AttributeError: Boxes instance has no attribute 'varname'
Thanks
You can use hasattr and getattr, although many might suggest you should pursue a different solution.
def addToBox(self, value):
box = self.determineBoxToUse(value)
## box = 2
varname = "box{}".format(box) ## box2
if hasattr(self, varname):
getattr(self, varname).insert(0,value)
Demo:
>>> class Boxes:
def __init__(self):
self.box1 = []
self.box2 = []
#....
self.box10 = []
def addToBox(self, value):
box = self.determineBoxToUse(value)
## box = 2
varname = "box{}".format(box) ## box2
if hasattr(self, varname):
getattr(self, varname).insert(0,value)
def determineBoxToUse(self, value):
## for this example returns 2
return 2
def dumpBox(self):
print self.box2
>>> Boxes = Boxes()
>>> Boxes.addToBox("123.001234")
>>> Boxes.dumpBox()
['123.001234']
>>>

How to call method of other object in Python?

I am new in python, and I have small problem, I have two classes, and it wrotes this : set_gyro_angle() takes exactly 1 argument (2 given) how can I call set_gyro_angle() method from Machine method?
class Gyro(object):
"""gyroskop senzor"""
def __init__(self,gyro_start_angle = 0):
self.gyro_angle = 0
def get_gyro_angle():
return self.gyro_angle
def set_gyro_angle(angle):
self.gyro_angle = angle
return 0
class Maschine(object):
def __init__(self, state = "normal",length = 10,width = 15):
self.length = length
self.width = width
self.gyro = Gyro()
def setPoint(self,alpha):
self.gyro.set_gyro_angle(alpha)
return 0
Main:
maschine = Maschine()
maschine.setPoint()
If you want to create an instance method, you need to add an extra argument that will be a pointer to your instance. Usually it's self:
class Gyro(object):
"""gyroskop senzor"""
def __init__(self,gyro_start_angle = 0):
self.gyro_angle = 0
def get_gyro_angle(self):
return self.gyro_angle
def set_gyro_angle(self, angle):
self.gyro_angle = angle
return 0
And i think you want setPoint to be like this:
def setPoint(self, alpha):
self.gyro.set_gyro_angle(alpha)
All of your instance methods should have another parameter, self, before the others; this is the instance itself, and is passed automatically:
def set_gyro_angle(self, angle):
Alternatively, skip the setter:
self.gyro.gyro_angle = alpha
Machine.gyro.set_gyro_angle(45)
However you need to fix your code by adding the self parameter as the first parameter of your class methods.

Categories

Resources