Best practice in Python for accessing self in another Class - python

For example if I have 2 classes Tree and Node, in the add_node method in Tree, I can add self referring to the Tree into the Node object as shown below. This is done for convenience when accessing other parts of the Tree with just the keys once in the Node object (i.e. can just use keys). However, I was wondering if this was considered best practice since the IDE (PyCharm) also screams Unresolved attribute reference 'tree' for class 'Node' so might be not a great sign as well.
class Tree:
def __init__(self):
self.nodes = {}
def add_node(self, count):
new_node = Node()
new_node.tree = self
self.nodes[count] = new_node
class Node:
def __init__(self):
# Some data e.g. parent, children index
pass

Pass the tree in the initializer for the Node
class Node:
def __init__(self, tree):
self.tree = tree
And when creating the node:
...
new_node = Node(self)
self.nodes[count] = new_node
...

Related

how is the object using a variable which is not inside the class or defined anywhere

In this code the object of class Node is using a variable next which is not defined anywhere and the code is still working HOW?How is the object using a variable which is not defined in its class
class Node:
def __init__(self, data):
self.data = data
class LinkedList:
# Function to initialize head
def __init__(self):
self.head = None
# Function to reverse the linked list
def reverse(self):
prev = None
current = self.head
while(current is not None):
next = current.next
current.next = prev
prev = current
current = next
self.head = prev
# Function to insert a new node at the beginning
def push(self, new_data):
new_node = Node(new_data)
new_node.next = self.head
self.head = new_node
# Utility function to print the linked LinkedList
def printList(self):
temp = self.head
while(temp):
print(temp.data)
temp = temp.next
llist = LinkedList()
llist.push(20)
llist.push(4)
llist.push(15)
llist.push(85)
print( "Given Linked List")
llist.printList()
llist.reverse()
print ("\nReversed Linked List")
llist.printList()
While in most strongly typed languages this is not possible, Python allows instance attributes to be defined even after the instance has been created and the constructor has run. As long as code does not reference an attribute before it has been defined, there is no issue. See also: Can I declare Python class fields outside the constructor method?
In this particular case the following code would produce an error:
node = Node(42)
if node.next: # Attribute error
print("42 is not the last node")
else:
print("42 is the last node")
However, the only place where new node instances are created is in the push method of the LinkedList class:
def push(self, new_data):
new_node = Node(new_data)
new_node.next = self.head
self.head = new_node
As you can see, the next attribute is defined immediately after the node is constructed. So in practice, every node in a linked list will have a next attribute.
Best Practice?
It is open for debate whether this coding practice is advisable. For instance, Pylint has a rule defining-attr-methods which by default will raise a warning when attributes are defined outside of __init__, __new__, setUp, or __post_init__.
Alternative
In this scenario I would certainly prefer to define the next attribute in the constructor, and give the constructor an extra, optional parameter with which next can be initialised:
class Node:
def __init__(self, data, nxt=None):
self.data = data
self.next = nxt
With this change, the push method of the LinkedList class can be reduce to just:
class LinkedList:
# ...
def push(self, new_data):
self.head = Node(new_data, self.head)
That looks a lot more elegant.
Unrelated, but I would also let the constructor of LinkedList accept any number of values to initialise the list with:
class LinkedList:
def __init__(self, *values):
self.head = None
for value in reversed(values):
self.push(value)
Now the main code could create a list with 4 values in one go:
llist = LinkedList(85, 15, 4, 20)

Alleged misunderstanding in using super [duplicate]

I have to do an unrolled linked list for one of my classes. I'm new to python, but not to programming, and for some reason I cannot get around this little problem!
I have a class Node that is to be the node object used within the unrolled linked list. The unrolled linked list class performs all the operations on the Node class.
class UnrolledLinkedList(object):
""" INNER NODE CLASS """
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
""" END NODE CLASS """
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = Node()
""" OTHER FUNCTIONS OF UNROLLEDLINKEDLIST CLASS """
The problem comes at the last line of the UnrolledLinkedList class' init function: "global name Node is not defined". I double checked my indentation and looked all over the internet for examples of something like this, but couldn't find any. Would someone mind explaining to me what's wrong?
Methods do not include their class as a scope to be searched. If you want this to work then you will need to use either UnrolledLinkedList.Node or self.Node instead.
The inner class Node is a member of the class UnrolledLinkedList and can only be accessed via self.
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = self.Node()
Use:
self.head = self.Node()
and it works.
A class does not create its own name space. Using self.Node(), Python first searches all attributes of the instances. Since it does not find the name Node there, it it searches the class UnrolledLinkedList for Node.
Alternatively, you can use the class name directly:
UnrolledLinkedList.Node()
You can achieve the same without nesting the class Node:
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
class UnrolledLinkedList(object):
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = Node()
Qualify Node() with self:
class UnrolledLinkedList(object):
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = self.Node()
Python needs to qualify references to things. In this case, you could either say UnrolledLinkedList.Node() or self.Node().

How to move a function from a class to another class without passing the calling object

I have the following implementation for the data structure (Trie) and it is working as expected. I have the main class Node and a wrapper class Trie. I want to move the function _insert() from the Node class to Trie class to keep Node as simple as possible. But I am facing many issues such as class Trie has no object nodes and no object word. Is there a way to do it without passing the calling object from the main in ?
Expectations:
trie.insert("Hi")
All implementation of insert() in class Trie
class Node:
def __init__(self):
self.word = None
self.nodes = {}
def _insert(self, word, string_pos=0):
current_lettter = word[string_pos]
if current_lettter not in self.nodes:
self.nodes[current_lettter] = Node()
if(string_pos + 1 == len(word)):
self.nodes[current_lettter].word = word
else:
self.nodes[current_lettter]._insert(word, string_pos + 1)
return True
class Trie:
def __init__(self):
self.root = Node()
def insert(self, word):
self.root._insert(word)
trie = Trie()
trie.insert("Hi")
class Trie (as implemented) has no object nodes and no object word.
class Trie has only root.nodes and root.word objects.

Tree class implementation with node and leaf

tree = Node ("one",
Node ("two", Leaf ("three"), Leaf ("four")),
Node ("five", Leaf ("six"), Leaf ("seven")))
Trying to declare a Tree class with 2 subclasses Node and Leaf to handle the tree object
class Tree:
def __init__(self, root):
self.root = root
# some functions
#def inorder(self, visitor):
# Node.inorder(self.left, visitor)
# visitor(self.data)
# Node.inorder(self.right, visitor)
#def fns(tree):
# return
class Node (Tree):
def __init__(self, value, left, right):
self.left = left
self.right = right
self.value = value
class Leaf (Tree):
def __init__(self, value):
self.value = value
Would this be the correct implementation?
This would be a correct implementation of binary tree. But keep in mind one thing, if you ever change root of the tree you will need to update every single node and leaf in that tree.
self.root is a bad idea in case you would create subtrees which you would later add to another tree. But if you are not planning to do something like this this would be a good idea.

Python class and variables

Learning Python and I ran into some problems when I was working on making a linked list class.
This is just a quick node and dirty node class. In java I would of down private Node next and private int val but I only knew of global as the python cousin. How does this look?
#class Node class
class Node(object):
global next
global val
def __init__(self):
next
val
def setNext(self, aNext):
self.next = aNext
def getNext(self):
return self.next
def setVal(self, aVal):
self.val = aVal
def getVal(self):
return self.val
Then I tried to use a Node in another class with
from Node import *
head = Node()
How ever I am getting an error of undefined variable. Sorry for the simple question just new to python. Appreciate the help.
I would implement this this way:
class Node(object):
def __init__(self, next=None, val=None):
self.next = next
self.val = val
That's it. No getters or setters - Python doesn't use them. Instead, you refactor into a property if you need to move away from the basic attribute reference logic.
You can then create nodes with or without values or successors:
tailnode = Node()
tailnode.val = 'foo'
midnode = Node(val='bar')
midnode.next = tailnode
headnode = Node(val='baz', next=midnode)
You don't need the "global val" / "global next" .. It's a mistake even.
instead just write
val = None
next = None
and initiate them in the __init__()
Meaning, the first lines in your class should be like:
class Node(object):
# You can choose whether to initialize the variables in the c'tor or using your setter methods
def __init__(self, val=None, next=None):
self.next = next
self.val = val
If you really want private variables in Python… then you don't want private variables, and should read Peter DeGlopper's answer.
If you still really, really want private variables in Python… well, you can't have them. But you can have "cooperatively private" variables—variables that nobody will find unless they go looking for them, and that won't clutter the screen when you introspect things in the interpreter, and so on, and, most importantly, that Python programmers know, by convention, that they aren't supposed to touch. All you have to do is start the name with an underscore.
However, your code isn't creating member variables at all, for a number of reasons.
First, global does not declare or define a variable; all it does is tell Python, "when you see this variable later, don't use the normal rules to figure out if it's local or global, always use the global copy". You still have to assign a value to the variable somewhere; otherwise, you'll get a NameError.
Next, variables that you assign in the class definition are class members—similar to Java's static members, although not identical. Each class member is shared by all instances of the class. That's not what you want here; each Node is supposed to have its own separate val and next, not share one with all other Nodes, right?
Normal instance member variables are always accessed through dot syntax—as self.foo from inside the class's methods, or as spam.foo from outside.
So, where do you declare those? You don't. Python doesn't declare anything. You can add new members to an object at any time. The usual way to create a standard set of instance members is in the __init__ method:
class Node(object):
def __init__(self):
self._next = None
self._val = None
def setNext(self, aNext):
self._next = aNext
def getNext(self):
return self._next
def setVal(self, aVal):
self._val = aVal
def getVal(self):
return self._val
But really, you can just let the setters create them. That way, you'll catch the error if someone calls getNext without having called setNext first (which is, I assume, illegal).
class Node(object):
def setNext(self, aNext):
self._next = aNext
def getNext(self):
return self._next
def setVal(self, aVal):
self._val = aVal
def getVal(self):
return self._val
Or, alternatively, force the user to initialize the object with valid values at construction time:
def __init__(self, next, val):
self._next = next
self._val = val
Again, there's no good reason to use setters and getters in the first place in Python.
So, the simplest implementation of your class is:
class Node(object):
pass
While the most Pythonic is:
class Node(object):
def __init__(self, next, val):
self.next = next
self.val = val
… which, you'll notice, is Peter DeGlopper's answer, which, as I said at the start, is probably what you want. :)
Python doesn't really use private variables.
Something like this would be best:
class Node(object):
def __init__(self):
self.val = None
self.next = None
Then, you make and set the node like this:
>>> node = Node()
>>> node.val = 5
>>> node2 = Node()
>>> node2 = 1
>>> node.next = node2
>>> node.next.val
1
If you want to create node with Node(5, Node(1)), use:
class Node(object):
def __init__(self, value=None, next=None):
self.value = value
self.next = next

Categories