Python accessing instance variable dynamically in method - python

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']
>>>

Related

In Python, why is my deepCopy still having its values changed?

I have made a class in python and I am trying to create a deepCopy of it, however the 'deepCopy' still has its values changed when I modify the original.
class boardState():
def __init__(self, board, orient,actions,expl):
self.board
self.orientations = orient
self.actions = actions
self.explored = expl
def __deepcopy__(self):
return boardState(self.board, self.orientations, self.actions, self.explored)
board = []
orientations = {} #this stores the orientations of the cars in the current problem.
actions = [] #this is a list of the moves made to get to this current position.
explored = []
^ above is the class that I am using and want to make a copy of.
referencestate = copy.deepcopy(state)
print(id(referencestate))
print(id(state))
^ after running this line, it's shown that they have the same id, I would like to make them Independant.
Any help would be greatly appreciated!
I think this deepcopy just get a new class for boardState but the id(state.board) = id(referencestate.board), because the original object is used directly when creating the class .
If you do not want to change the original value, do not pass the parameter directly.Use a copy of them. You can try to use ->state=boardState(board[:], dict.copy(orientations), actions[:], explored[:]) #this use shallow.
Take a look at the code below ->
import copy
class boardState():
def __init__(self, board, orient,actions,expl):
self.board = board
self.orientations = orient
self.actions = actions
self.explored = expl
"""
def __deepcopy__(self, memo):
return boardState(self.board[:], dict.copy(self.orientations), self.actions[:], self.explored[:])
def __deepcopy__(self):
return boardState(self.board, self.orientations, self.actions, self.explored)
"""
def __deepcopy__(self, memo):
dpcpy = self.__class__
memo[id(self)] = dpcpy
for attr in dir(self):
if not attr.startswith('_'):
value = getattr(self, attr)
setattr(dpcpy, attr, copy.deepcopy(value, memo))
return dpcpy
board = [[1]]
orientations = {"6":"h"} #this stores the orientations of the cars in the current problem.
actions = [2] #this is a list of the moves made to get to this current position.
explored = [3]
state1=boardState(board, orientations, actions, explored)
state2=boardState(board[:], dict.copy(orientations), actions[:], explored[:])
referencestate = copy.deepcopy(state1)
print(id(referencestate))
print(id(state1))
print(id(state2))
print(id(state1.board))
print(id(state2.board))
print(id(state1.board) == id(state2.board))
print(id(referencestate.board))
print(id(state1.board) == id(referencestate.board))
print(id(state1.board[0]))
print(id(state2.board[0]))
print(id(state1.board[0]) == id(state2.board[0]))
print(id(referencestate.board[0]))
print(id(state1.board[0]) == id(referencestate.board[0]))
try running

test equality for pygame.font.Font

I was trying to make a font cache like this:
from ._utils import SizedDict
def copysurf(x,mod):
if mod:
return cs(x)
return ImmutableSurface(x)
class FontCache:
def __init__(self):
self.cache = SizedDict(20)
self.fmisses = 0
self.fhits = 0
self.cmisses = {}
self.chits = {}
def getfor(self,font,char,aa,color):
if font not in self.cache:
self.cmisses[font] = 0
self.chits[font] = 0
self.cache[font] = SizedDict(300)
if char not in self.cache[font]:
self.cache[font][char] = font.render(char,aa,color)
return self.cache[font][char].copy()
fontCache = FontCache()
(Sized dicts are also made by me,they are dicts that deletes the oldest entry if it exeeds the capacity specified in the constructor.)
But the problem arises when i tried to cache the font.You see,
pygame.font.SysFont("Courier",22) == pygame.font.SysFont("Courier",22)
is false,and the hash() function returned different values.As i know,there aren't any methods that returns the original NAME of the font,and we can't test equality by merely knowing the size and the bold and italic flags.
Any thoughts?
You could subclass pygame.font.Font, store the metadata when the font is created. Something like this:
import pygame
pygame.init()
class Font(pygame.font.Font):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.properties = (args, tuple(kwargs.items()))
def __hash__(self):
return hash(self.properties)
def __eq__(self, other):
return self.properties == other.properties
#classmethod
def construct(cls, fontpath, size, bold, italic):
font = cls(fontpath, size)
font.strong = bold
font.oblique = italic
return font
# Prints True
print(
pygame.font.SysFont("Courier", 22, constructor=Font.construct)
== pygame.font.SysFont("Courier", 22, constructor=Font.construct)
)

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 2.7) Access variable from class with accessor/mutator

(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.

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

Categories

Resources