Checking if contained object has changed - python

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)

Related

A method inside a class that calls another method

I need help with the below code. I want to use the get_skies, get_high, and get_low method to call the set_skies, set_high, and set_low methods, respectively, and then return the value for init_skies, init_high, and init_low, respectively.
This is what I have got so far:
class WeatherForecast():
def set_skies(self, init_skies):
return init_skies
def set_high(self, init_high):
return init_high
def set_low(self, init_low):
return init_low
def get_skies(self):
self.set_skies()
def get_high(self):
self.set_high()
def get_low(self):
self.set_low()
In python attributes of class are publically accessible.
You don't need to use getter or setters for attributes unless you want to perform some kind of preprocessing or mutation of the attribute
In your case, you can try this,
class WeatherForecast():
def __init__(self, init_skies, init_low, init_high):
self._init_skies = init_skies
self._init_low = init_low
self._init_high = init_high
#property
def skies(self):
return self._init_skies
#property
def high(self):
return self._init_high
#property
def low(self):
return self._init_low
#skies.setter
def skies(self, value):
self._init_skies = value
#high.setter
def high(self, value):
self._init_high = value
#low.setter
def low(self, value):
self._init_low = value
w = WeatherForecast(1, 2, 3)
print(w.skies, w.low, w.high) # --> print the values
# Set the values
w.skies = 10
w.low = 20
w.high = 30
print(w.skies, w.low, w.high) # --> print the updated values

Python classes and types

I think I'm misusing the concept of subclass. I'm working on a hobby project with Grids and Cells.
What I have, is the implementation of a Cell class, and its subclass HexCell which basically redefines many of the attributes/methods like so:
class Cell:
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
self.links = set()
self.neighbors = 4*[None]
def __repr__(self):
return f'Cell #({self.row},{self.col})'
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
Then I have a subclass that is the HexGrid which follows a similar structure with new parameters.
class HexCell(Cell):
def __init__(self, r_out, th_around):
# I'm indexing Hex cells around a center cell
# instead of by rows and columns; Prefixed hex
# as they follow the hexagon, and not regular polar coordinates.
self.hex_r = r_out
self.hex_th = th_around
self.neighbors = 6*[None]
self.links = set()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
to_dir = to_dir % 6
if (self.neighbors[to_dir] is None):
self.neighbors[to_dir] = other
other.neighbors[to_dir - 3] = self
# Hexagonal grids share neighbors.
other_1 = other.neighbors[to_dir - 2]
if (self.neighbors[to_dir - 1] is None) & (other_1 is not None):
self.bind(other_1, to_dir - 1)
other_5 = other.neighbors[to_dir - 4]
if (self.neighbors[to_dir - 5] is None) & (other_5 is not None):
self.bind(other_5, to_dir - 5)
In this case, the method self.link(other) is shared, but other attributes change from rectangular grid to hexagonal like the locaion from (row, col) to (hex_r, hex_th), or neighbors as a 4-list or 6-list. Thus I'd like these attributes to be dependent on a another cell-type attribute and transferred down to the subclass.
Correct use of subclassing needs to obey the following substitution principle:
If there are some objects x_1 of type T_1 and x_2 of type T_2 such that issubclass(T_2, T_1) == True, then any property that applies to x_1 must also apply for x_2.
In other words, you expect subclassing to implement new behaviours, not to change existing behaviours.
In you example, the change of coordinate system itself is a change of behaviour and thus HexCell should not inherit from Cell.
What you can do is create a base class BaseCell that encapsulates the common behaviour between Cell and HexCell and inherit from it.
class BaseCell:
def __init__(self):
self.links = set()
self.neighbors = []
def add_neighbor(self, other):
self.neighbors.append(other)
def link(self, other, bidirectional=True):
self.links.add(other)
if bidirectional:
other.link(self, bidirectional=False)
class Cell(BaseCell):
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
super().__init__()
def __repr__(self):
return f'Cell #({self.row},{self.col})'
class HexCell(Cell):
def __init__(self, r_out, th_around):
self.hex_r = r_out
self.hex_th = th_around
super().__init__()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
...
Your Cell class is in fact not an abstract "Cell", but a square cell in two-dimensional space (has exactly 4 neighbours, has "row" and "col" position). Such cell may not be subclassed by a hex cell, because hex cell is just a different type of cell : )
As you noticed, the only common things are link() method and links attribute. If you insist on subclassing, you could create something like:
class LinkedObject():
def __init__(self):
self.links = set()
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
class SquareCell(LinkedObject):
# "Cell" class here
class HexCell(LinkedObject):
# HexCell here

Print dict with custom class as values wont call their string method?

I was messing around with classes in python and wrote 2 little ones:
class ClaElement:
start = None
end = None
basesLeft = None
orientation = None
contig = None
size = None
def __init__(self, contig, start, end, orientation, basesLeft=None):
self.contig = contig
self.start = start
self.end = end
self.orientation = orientation
self.basesLeft = basesLeft
self.size = self.end - self.start
def __str__(self):
return "{ClaElement: "+str(self.contig)+"_"+str(self.start)+"_"+str(self.end)+"_"+str(self.orientation)+"}"
def getSize(self):
return self.size
class ClaCluster:
contig = None
clusterElements = []
def __init__(self, contig, firstElement):
self.contig = contig
self.addElement(firstElement)
def addElement(self, claElement):
self.clusterElements.append(claElement)
def getFirst(self):
return self.clusterElements[0]
def getLast(self):
return self.clusterElements[-1]
def getElements(self):
return self.clusterElements
def getContig(self):
return self.contig
def __str__(self):
return "{ClaCluster: "+str(self.contig)+" "+str(len(self.clusterElements))+" elements}"
And my test-main:
from ClaElement import ClaElement
from ClaCluster import ClaCluster
if __name__ == '__main__':
ele = ClaElement("x",1,2,"left")
claDict = dict()
cluster = ClaCluster("x", ele)
claDict["hello"] = cluster
print(claDict)
print(claDict["hello"])
print(ele)
This leads to the following output:
{'hello': <ClaCluster.ClaCluster object at 0x7fe8ee04c5f8>}
{ClaCluster: x 1 elements}
{ClaElement: x_1_2_left}
Now my question is why is the output of my first print the memory address even though I provided a functioning string-method for my class ClaCluster? Is there a way to get the method invoked when I am printing the dictionary or do I have to iterate by hand?
The __str__() method of the built-in dict type uses the __repr__() method of your class, not __str__(). Simply rename your method, and all should work fine.

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.

Access python data from C(++)

I write some python functions to read/write simple spatial data like points, polygons, triangulated surfaces into/from a simple data structure in Python. I'm aware there are some possibilities to access those data structure from C or C++ like in this post:
Passing Python list to C++ vector using Boost.python
However, I'm not an experienced programmer using templates and I would like to start first with an easy way - if it exist. So if I have a list of point objects (see simple class definition), how would I access this list from a C-Program reading the respective variables.
class point3d():
def __init__(self, objectName, version, id=[], xyz=[], prop=[], unit=[], val=[]):
self.id = id
self.xyz = xyz
self.prop = prop
self.unit = unit
self.val = val
dim = len(self.xyz)
self.objectName = objectName
self.version = version
if dim == 1:
self.x = self.xyz[0]
elif dim == 2:
self.x = self.xyz[0]
self.y = self.xyz[1]
elif dim == 3:
self.x = self.xyz[0]
self.y = self.xyz[1]
self.z = self.xyz[2]
def __del__(self):
pass
def getObjectName(self):
return self.objectName
def getVersion(self):
return self.version
def getXYZ(self):
return self.xyz
def getDim(self):
return self.dim
def getProp(self):
return self.prop
def getUnit(self):
return self.unit
def getVal(self):
return self.val
def getId(self):
return self.id
If you want to call a C/C++ function from your python program, ctypes is the way to go.

Categories

Resources