I am working on a grid-generator that takes x- and y-coordinates as input. When initiating, this node is stored using a @classmethod
(storage is a set
, see MWE) during which a unique index is provided. As part of __init__
, there is a check if there is already a node with the same (x,y)-coordinates; if so, no new node should be created.
So far, so good. The problem arises when I want to return the previously defined node when the (x,y)-coordinates are already assigned to one. So when I initiate a node with previously defined (x,y)-coordinates, I want that previously defined node-object to be returned. As an example:
n1 = Node(x=0, y=0)
n2 = Node(x=0, y=0)
In this example, both n1
and n2
should contain exactly the same object, and not a copy with the same details. Thus, n1 == n2
should return True
, as the objects are identical. This is necessary for further computations (left out for clarity reasons).
Below a MWE of my Node
-class:
class Node:
__nodes = set() # Here, Node-objects are stored
__node_idx = None
def __init__(self, x, y):
# This is the check if the (x,y)-coordinates are already assigned to a node.
if (x, y) not in [n.coordinates for n in self.__nodes]:
self.x = x
self.y = y
self.set_idx(self)
self.store_node(self)
# Should here be something like an 'else'-statement?
@classmethod
def set_idx(cls, node):
"""Function to set unique index based on the number of nodes."""
# There is a procedure to determine this index, but that does not matter for the question.
node.__node_idx = idx
@classmethod
def store_node(cls, node):
cls.__nodes.add(node)
@property
def index(self):
return self.__node_idx
@property
def coordinates(self):
return self.x, self.y
Stating that self
becomes this previously defined node is not working; I have considered it. Thus as else
-statement in the __init__
:
...
else:
self = [n for n in self.__nodes if n.coordinates == (x, y)][0]
I have looked at the __hash__
-method, but I am not familiar with it and I do not know if this is where the solution to my problem lies.
I am using Python 3.7. Thank you very much for any help; really appreciated!
Frequently, in practice, there is a need to construct a new object so that it is initially the same as some existing object. To do this, either we can use Object.clone () method or define a constructor that takes an object of its class as a parameter. In java, a method can return any type of data, including objects.
Note: When an object reference is passed to a method, the reference itself is passed by use of call-by-value. However, since the value being passed refers to an object, the copy of that value will still refer to the same object that its corresponding argument does.
Returning Objects. In java, a method can return any type of data, including objects. For example, in the following program, the incrByTen( ) method returns an object in which the value of a (an integer variable) is ten greater than it is in the invoking object.
C# | Method returning an object. In C#, a method can return any type of data including objects. In other words, methods are allowed to return objects without any compile time error. Example 1: // C# program to illustrate the concept.
What about using the factory pattern?
class Node:
def __init__(self, x, y):
self.x = x
self.y = y
class NodeFactory:
def __init__(self):
self.nodes = set()
def create_node(self, x, y):
for node in self.nodes:
if node.x == x and node.y == y:
return node
node = Node(x, y)
self.nodes.add(node)
return node
factory = NodeFactory()
n1 = factory.create_node(1, 5)
n2 = factory.create_node(1, 5)
n3 = factory.create_node(2, 1)
print(n1)
print(n2)
print(n3)
Output:
<__main__.Node object at 0x7fa595aa6640>
<__main__.Node object at 0x7fa595aa6640>
<__main__.Node object at 0x7fa595aa6100>
As you can see n1
and n2
are the same object here, so:
>>> n1 == n2
True
You can override the __new__
method of the class to control the creation of the nodes.
We store the existing nodes in a class attribute, a dict with the coordinates as keys and the Node objects as values.
__new__
looks if there already is a node with these coordinates in this dict. If yes, it just returns the already existing node. If not, it creates and returns a new one.
Depending on what you do in it, it may be better not to run __init__
again (or maybe not all parts of it) for already existing nodes. So, we just set an _already_initialized
flag on the instance, that will be False
at creation time, and that will be changed to True
by __init__
. This way, you can completely control what to do the first time a node is initialized, and the following times.
The code, with a few print
s included to show what's going on:
class Node:
_nodes = {}
def __new__(cls, x, y):
if (x, y) in cls._nodes:
print('Reusing', cls._nodes[(x, y)])
return cls._nodes[(x, y)]
new_node = super().__new__(cls)
new_node._already_initialized = False
print(f'New node created for x={x}, y={y}')
cls._nodes[(x, y)] = new_node
return new_node
def __init__(self, x, y):
if not self._already_initialized:
self.x = x
self.y = y
self.set_idx(self)
self._already_initialized = True
print(self, 'initialized')
else:
print(self, 'was already initialized, leaving as it is')
@classmethod
def set_idx(cls, node):
"""Function to set unique index based on the number of nodes."""
# There is a procedure to determine this index, but that does not matter for the question.
pass
@property
def index(self):
return self.__node_idx
@property
def coordinates(self):
return self.x, self.y
def __repr__(self):
return f'Node: x={self.x}, y={self.y}'
Demo:
print('Creating n1')
n1 = Node(x=0, y=0)
print('\nCreating n2')
n2 = Node(x=0, y=0)
print('\nCreating n3')
n3 = Node(1, 1)
print(f'\nn1 == n2: {n1 == n2}')
print(f'n1 is n2: {n1 is n2}')
print(f'\nNode._nodes: {Node._nodes}')
Output:
Creating n1
New node created for x=0, y=0
Node: x=0, y=0 initialized
Creating n2
Reusing Node: x=0, y=0
Node: x=0, y=0 was already initialized, leaving as it is
Creating n3
New node created for x=1, y=1
Node: x=1, y=1 initialized
n1 == n2: True
n1 is n2: True
Node._nodes: {(0, 0): Node: x=0, y=0, (1, 1): Node: x=1, y=1}

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With