Python creating static objects which are shared amongst all class objects - python

I have a class node something like this. It's a typical node object for a graph.
class Node(object):
def __init__(self, data, edges = []):
super(Node, self).__init__()
self.data = data
self.edges = edges
self.visited = False
def addEdge(self, *args):
print(self)
self.edges.extend(args)
print(self.edges)
I create two objects like this -
one = Node(1)
two = Node(2)
Next I add a pointer of two to one using the addEdge method defined above -
one.addEdge(two)
Now comes the surprising bit. When I check the values of one.edges and two.edges I get this -
one.edges
[<main.Node object at 0x109ed3e50>]
two.edges
[<main.Node object at 0x109ed3e50>].
If you see both the objects have gotten the value. I'm quite puzzled at this and have no idea why this is happening. Is this how python behaves? If so can you explain this behaviour?

You need to be careful when using an array literal as a default value because you don't get a new array with each instance — you get a reference to the same one. In your example you will see:
>> one.edges is two.edges
True
You need to do something to make sure you get a new array each time. One things you can do is:
self.edges = list(edges)
Another option is:
def __init__(self, data, edges = None):
if edges is None:
self.edges = []
else:
self.edges = edges
But edges is still mutable so it may also lead to subtle bugs if the caller is not expecting it to be changed.

Related

Remove router function in python

Hi just wondering if anyone can tell me why this code isn't working
It's giving an internal server error
Thanks
#app.post("/removerouter/")
def removerouter(name: str):
Graph.remove_node(name)
return "success"
And this is the function inside Graph
class Graph:
def __init__(self):
self.nodes = []
self.edges = []
def remove_node(self, name):
self.nodes.remove(name)
for Edges in self.edges:
if name in Edges:
self.edges.remove(Edges)
Based on the code that you posted, I would say that the issue is somehow related to how you remove your edge(s) in the for loop.
When you delete a list element using the remove()function in Python, it changes the remaining element's indexing.
For more details and alternatives, see this SO question.
I also don't understand why you are using an iterator variable called Edges in your for loop. Python variables shall always start with a lowercase letter in order not to clash with any existing (or future) class name.
I would rather do something like this:
class Graph:
def __init__(self):
self.nodes = []
self.edges = []
def remove_node(self, name):
self.nodes.remove(name)
self.edges = [edge for edge in self.edges if name not in edge]
Note that I'm using a list comprehension here to assign a new list to self.edges.
If you want to avoid list comprehension, you could also keep your for loop and first store the indexes of the edges that need to be removed. Then, for each index, you can simply do del self.edges[index].

In Python, why does .append give different results than using +? [duplicate]

This question already has answers here:
Why does using `arg=None` fix Python's mutable default argument issue?
(5 answers)
Closed 3 years ago.
I want to implement a very simple tree data structure in Python.
I want to make it so that everytime I add a new node and I specify its parent, then it is automatically added to the children attribute of its parent.
I have two different ways to do it, one works and one doesn't but I don't understand why.
class Node():
def __init__(self, value, parent = None, children = []):
self.value = value #This is for us to see the name
self.parent = parent #Parent node
self.children = children #List of child nodes
#Set this Node as a children of its own parent
if not parent == None:
#self.parent.children.append(self) <--- wrong code
self.parent.children = self.parent.children + [self]
def __repr__(self):
return str(self.value)
tree = Node("tree")
branch1 = Node("branch1", parent = tree)
branch2 = Node("branch2", parent = tree)
leaf = Node("leaf", parent = branch1)
Here is what I get with the code as is and what I would get if I replace the last line of __init__ with the commentated line.
print(tree.children)
#[branch1, branch2] <--- expected
#[branch1, branch2, leaf] <--- with wrong code
print(branch1.children)
#[leaf] <--- expected
#[branch1, branch2, leaf] <--- with wrong code
Using the .append method adds the node not only to the list children of its parent but to everybody. Even if I define a new Node("other") completely detached from the others. Why is that?
The problem is in the use of mutable default value:
def __init__(self, value, parent = None, children = []):
The empty list [] gets created only once, when the function is defined, and all invocations share the same list! This is why append to one list of children modifies all of them - because they all are one and the same list object. When you use + to append to the list, you work around the above bug because you re-create the list every time, thus unsharing the children objects.
The correct solution is to replace children=[] with something like:
def __init__(self, value, parent=None, children=None):
if children is None:
children = []
That will guarantee the creation of a new list for children, and then append and + should have the same result.

do python custom linked-lists carry all their attributes in each node?

I am trying to translate a Python voronoi diagram generator into Java. However, I'm having trouble figuring out how the Arc class works.
I've implemented linked lists in Java before, but the implementation here seems very different from what I'm used to. I'm not completely sure it is a linked list, it just seems like it because of the way the class is used later on.
Does the Arc class here carry all it's attributes in each node?
The Arc class:
class Arc:
p = None
pprev = None
pnext = None
e = None
s0 = None
s1 = None
def __init__(self, p, a=None, b=None):
self.p = p
self.pprev = a
self.pnext = b
self.e = None
self.s0 = None
self.s1 = None
Example of Arc class in use:
def process_event(self):
# get next event from circle pq
e = self.event.pop()
if e.valid:
# start new edge
s = Segment(e.p)
self.output.append(s)
# remove associated arc (parabola)
a = e.a # <-- a is an "Arc" type
if a.pprev is not None:
a.pprev.pnext = a.pnext
a.pprev.s1 = s
if a.pnext is not None:
a.pnext.pprev = a.pprev
a.pnext.s0 = s
# finish the edges before and after a
if a.s0 is not None: a.s0.finish(e.p)
if a.s1 is not None: a.s1.finish(e.p)
# recheck circle events on either side of p
if a.pprev is not None: self.check_circle_event(a.pprev, e.x)
if a.pnext is not None: self.check_circle_event(a.pnext, e.x)
There could be a mild nomenclature confusion in your question, but as long as I understand it correctly and for given example, the short answer is: yes.
Assuming by node you mean each instance of Arc type object, as constructed in the first snippet, all attributes are bound to an instance (and for that matter at least from this code) defining them also as class attributes above does not seem to serve any particular purpose or make a lot of sense.

Implementing Graph for Bayes Net in FSharp

I'm trying to translate a graph formulation from Python to F#
The python "Node" class:
class Node:
""" A Node is the basic element of a graph. In its most basic form a graph is just a list of nodes. A Node is a really just a list of neighbors.
"""
def __init__(self, id, index=-1, name="anonymous"):
# This defines a list of edges to other nodes in the graph.
self.neighbors = set()
self.visited = False
self.id = id
# The index of this node within the list of nodes in the overall graph.
self.index = index
# Optional name, most usefull for debugging purposes.
self.name = name
def __lt__(self, other):
# Defines a < operator for this class, which allows for easily sorting a list of nodes.
return self.index < other.index
def __hash__(self):
return hash(self.id)
def __eq__(self, right):
return self.id == right.id
def add_neighbor(self, node):
""" Make node a neighbor if it is not alreadly. This is a hack, we should be allowing self to be a neighbor of self in some graphs. This should be enforced at the level of a graph, because that is where the type of the graph would disallow it.
"""
if (not node in self.neighbors) and (not self == node):
self.neighbors.add(node)
def remove_neighbor(self, node):
# Remove the node from the list of neighbors, effectively deleting that edge from
# the graph.
self.neighbors.remove(node)
def is_neighbor(self, node):
# Check if node is a member of neighbors.
return node in self.neighbors
My F# class so far:
type Node<'T>= string*'T
type Edge<'T,'G> = Node<'T>*Node<'T>*'G
type Graph =
| Undirected of seq(Node*list Edge)
| Directed of seq(Node*list Edge *list Edge)
Yes, this does have to do with immutability. F#'s Set is an immutable collection, it is based on a binary tree which supports Add, Remove and lookup in O(log n) time.
However, because the collection is immutable, the add operation returns a new Set.
let originalSet = set [1; 2; 7]
let newSet = originalSet.Add(5)
The most purely functional solution is probably to reconstruct your problem to remove the mutability entirely. This approach would probably see you reconstruct your node class as an immutable data container (with no methods) and define the functions that act on that data container in a separate module.
module Nodes =
/// creates a new node from an old node with a supplied neighbour node added.
let addNeighbour neighbourNode node =
Node <| Set.add neighbourNode (node.Neighbours)
//Note: you'll need to replace the backwards pipe with brackets for pre-F# 4.0
See the immutable collections in the FSharp Core library such as List, Map, etc. for more examples.
If you prefer the mutable approach, you could just make your neighbours mutable so that it can be updated when the map changes or just use a mutable collection such as a System.Collections.Generic.HashSet<'T>.
When it comes to the hashcode, Set<'T> actually doesn't make use of that. It requires that objects that can be contained within it implement the IComparable interface. This is used to generate the ordering required for the binary tree. It looks like your object already has a concept of ordering built-in which would be appropriate to provide this behaviour.

Append to the same list

I am from a solid C/C++ background, so please bear with me if my question is silly.
Here I have two classes that declared to encapsulate data,
class Node:
Term = ""
TermInfo = []
def __init__(self,S,Info):
self.Term = S
self.TermInfo.append(Info)
class TermInfo:
DocID = 0
Freq = 0
def __init__(self,ID,F):
self.DocID = ID
self.Freq = F
and I was trying to manipulate them this way
Info = TermInfo(ID,0)
node = Node(j,Info)
Dict[j] = node
Basically I was trying to construct a dictionary that contains nodes that are made of a string "Term" and a list of "Terminfo", I was expecting that each node has its own copy. However, after I called the three lines twice in a row
Info = TermInfo(ID,0)
node = Node(j,Info)
Dict[j] = node
Info = TermInfo(ID,0)
node = Node(j,Info)
Dict[j] = node
I was surprised to see that the two lists of "TermInfo" were pointing to the same memory address and the second append() also changed the first list,So how do I make sure each node has its own copy instead of pointing to the same address? Thanks
Short answer:
create a new list in __init__.
def __init__(self,S,Info):
self.Term = S
self.TermInfo = [Info]
Long answer:
This has to do with how python looks up attributes. When you do: instance.attribute, python first looks for the attribute on the instance. If it's not found there, it looks for the attribute on the class. Here you have a list attribute on the class and you keep on appending to that attribute via the instance. So when you have the line:
self.TermInfo.append(whatever)
Python first looks at self. however, self doesn't have a TermInfo attribute, so then python looks at self.__class__ (Node in this case) for the TermInfo attribute -- and it finds it so it appends to the Node.TermInfo list.
Only attributes assigned in __init__ and after object construction are instance attributes. Attributes assigned in the class definition are class attributes, similar to static class members in other languages. So if you want to have a per-instance attribute, assign its initial value in __init__.

Categories

Resources