How to modify argument to function cleanly in Python? - python

I have the following code which I use to modify the node that is passed into the function recursively (the node is wrapped in an array so that the modification is persistent after the function returns):
Is there a better or cleaner way to modify the argument?
`
class node(object):
def __init__(self, value, next=None):
self._value = value
self._next = next
def __repr__(self):
return "node({0})".format(self._value)
#property
def next(self):
return self._next
def foo(a, b):
if b == 0:
print "setting to next node",
a[0] = a[0].next
print a
return
print a
foo(a, b-1)
print a
n = node(5, node(8))
foo([n], 2)
`
Question was answered in: How do I pass a variable by reference?

To modify something, that thing has to be mutable. Your node instances are mutable:
n = node(3)
assert n.value == 3
n.value = 5
assert n.value == 5 # it was modified!
Also, your function fails to return any values. In your case it may be a wrong approach. Also, I frankly don't see why you would use number (0, n - 1) where the .next value is referenced. These must be node instances, not numbers.
Apparently you're making a linked list implementation, and your foo function tries to remove n-th node by traversing a list. (Please take care to name your functions descriptively; it helps both you and people answering your question.)
That's how I'd do it:
class Node(object): # class names are usually TitleCase
def __init__(self, value, next=None):
self.value = value
self.next = next # no properties for simplicity
def __repr__(self):
return "node({0})".format(self.value)
def asList(node): # nice for printing
if not node:
return []
return [node.value] + asList(node.next)
def removeNodeAt(head_node, index):
"""Removes a node from a list. Returns the (new) head node of the list."""
if index == 0: # changing head
return head_node.next
i = 1 # we have handled the index == 0 above
scan_node = head_node
while i < index and scan_node.next:
scan_node = scan_node.next
i += 1
# here scan_node.next is the node we want removed, or None
if scan_node.next:
scan_node.next = scan_node.next.next # jump over the removed node
return head_node
It works:
>>> n3 = Node(0, Node(1, Node(2)))
>>> asList(removeNodeAt(n3, 2))
[0, 1]
>>> n3 = Node(0, Node(1, Node(2)))
>>> asList(removeNodeAt(n3, 1))
[0, 2]

Because the parameter your are operating is object. You could use __dict__ to change the whole property of the object. It is equivalent to change every property of the project. You could try the following code:
class node(object):
def __init__(self, value, next=None):
self._value = value
self._next = next
def __repr__(self):
return "node({0})".format(self._value)
#property
def next(self):
return self._next
def foo(a):
print "setting to next node\n",
a.__dict__ = getattr(a.next, '__dict__', None)
return
n = node(5, node(8, node(7)))
print n._value, '->' ,n._next._value
foo(n)
print n._value, '->' ,n._next._value
Hope this could help you.

Related

How can I overload the __repr__ method to display all items in a linked list Stack?

I've created my Node and Stack classes, but I can't figure out how I can display the repr in the Stack class in order to be able to print all items currently in the stack? I've been trying to concatenate the nodes but I'm not sure how since the Stack() doesn't allow iterating through the way a list does?
The stack works as it should, I just don't know how to display it's contents?
Here is my code:
class Stack:
class Node:
def __init__(self, elem, next):
self.elem = elem
self.next = next
def __repr__(self):
return str(self.elem)
def __init__(self):
self._stack = None
self._size = 0
def __repr__(self):
# *Not sure how to implement this properly*
s = ''
for i in range(self._size):
last = self._stack.elem
s += (str(last))+ ', '
self._stack.elem = self._stack.next
return
def push(self, elem):
if self._stack == None:
self._stack = self.Node(elem, None)
self._size += 1
else:
self._stack = self.Node(elem, self._stack)
self._size += 1
def pop(self):
if self._stack == None:
raise Exception ('This Stack is empty!')
else:
last = self._stack.elem
self._stack = self._stack.next
self._size -= 1
return last
def top(self):
return self._stack.elem
def isEmpty(self):
return self._size == 0
Example:
s= Stack()
s.push(4)
s.push(9)
s.push("joe")
s
joe, 9, 9,
Thank you in advance.
A way simpler implementation that avoids all the problems and pitfalls of your solution:
from typing import Iterable, Any
class Stack:
def __init__(self, xs: Iterable = None):
self._items = [] if xs is None else list(xs)
def push(self, elem: Any):
self._items.append(elem)
def pop(self) -> Any:
return self._items.pop()
def top(self) -> Any:
return self._items[-1]
def isEmpty(self) -> bool:
return not self._items
def __repr__(self) -> str:
typename = type(self).__name__
return f'{typename}({self._items})'
def __str__(self) -> str:
return ', '.join(str(x) for x in self._items)
s = Stack()
s.push(4)
s.push(9)
s.push("joe")
print(s)
print(repr(s))
But note that there's little use to a class like this over just using a list like a stack to begin with.
The output:
4, 9, joe
Stack([4, 9, 'joe'])
Note that this has the top element at the end, you could reverse it if you like of course.
If you insist on a working __repr__ for your specific implementation, using __repr__ as you intend in a non-standard way, something like this would work:
def __repr__(self):
p = self._stack
elems = []
while p is not None:
elems.append(p.elem)
p = p.next
return ', '.join(elems)
But note that there's several other issues with your implementation, other than this not being a correct __repr__, as previously pointed out here and in the comments. Your 'node' has a __repr__ which just returns its element value (which isn't a valid representation at all in most cases); you seem to be using __repr__ where you're really after __str__.
If this were an assignment in programming class, I'm not sure I'd award a passing grade, depending on what the aim was.

Why this code is not able call function inside another function in python

I am trying to implement Singly Linked List in Python. This is my _Node class:
#!/usr/bin/env python3
class _Node:
"""Node class to create new nodes"""
def __init__(self, data=None, next=None):
"""Construction of node"""
self._data = data
self._next = next
I have removed push(), pop() and other methods from this code sample. They all are working.
class LinkedList:
"""Singly Linked List implementation for storage"""
def __init__(self):
"""Construction of Linked List"""
self._head = None
self._size = 0
def __len__(self):
"""Return length of linked list."""
self._count = 0
self._current = self._head
while self._current:
self._count += 1
self._current = self._current._next
return self._count
def value_at(self, index):
"""Return Value at given index"""
self._current = self._head
self._index = index
count = ans = 0
while count<= self._index:
if self._current == None:
return "List is empty."
ans = self._current._data
self._current = self._current._next
count += 1
return ans
def value_n_from_end(self, n):
"""Get value of nth element starting from end"""
self.n=n
self.n = int(self.__len__() - self.n -1)
print(self.n) #print value as expected
self.value_at(self.n)
Now my problem is that i can get value from value_at() but unable to get value from value_n_from_end() outside the class.
Input:-
l = LinkedList()
print(l)
print(l.__len__())
print(l.value_at(1))
print(l.value_n_from_end(2))
Output:-
5-> 3-> 4-> 6-> //My Linked List
4 //Length of linked list
3 //Value return by l.value_at(1)
1 //This is the value which i want to pass in self.value_at(self.n)
None //This is returned from (l.value_n_from_end(2))
Value of l.value_n_from_end(2) should be same as l.value_at(1) i.e. 3. But i am missing something.
The value_n_from_end does not return anything. You should write:
return self.value_at(self.n)
Indeed, self.value_at(self.n) returns what you want in the function value_n_from_end but then you have to get it back to you by using a return statement. Otherwise it is bound to the function namespace.

Python inheriting base class variable [duplicate]

I'm trying to simplify one of my homework problems and make the code a little better. What I'm working with is a binary search tree. Right now I have a function in my Tree() class that finds all the elements and puts them into a list.
tree = Tree()
#insert a bunch of items into tree
then I use my makeList() function to take all the nodes from the tree and puts them in a list.
To call the makeList() function, I do tree.makeList(tree.root). To me this seems a little repetitive. I'm already calling the tree object with tree.so the tree.root is just a waste of a little typing.
Right now the makeList function is:
def makeList(self, aNode):
if aNode is None:
return []
return [aNode.data] + self.makeList(aNode.lChild) + self.makeList(aNode.rChild)
I would like to make the aNode input a default parameter such as aNode = self.root (which does not work) that way I could run the function with this, tree.makeList().
First question is, why doesn't that work?
Second question is, is there a way that it can work? As you can see the makeList() function is recursive so I cannot define anything at the beginning of the function or I get an infinite loop.
EDIT
Here is all the code as requested:
class Node(object):
def __init__(self, data):
self.data = data
self.lChild = None
self.rChild = None
class Tree(object):
def __init__(self):
self.root = None
def __str__(self):
current = self.root
def isEmpty(self):
if self.root == None:
return True
else:
return False
def insert (self, item):
newNode = Node (item)
current = self.root
parent = self.root
if self.root == None:
self.root = newNode
else:
while current != None:
parent = current
if item < current.data:
current = current.lChild
else:
current = current.rChild
if item < parent.data:
parent.lChild = newNode
else:
parent.rChild = newNode
def inOrder(self, aNode):
if aNode != None:
self.inOrder(aNode.lChild)
print aNode.data
self.inOrder(aNode.rChild)
def makeList(self, aNode):
if aNode is None:
return []
return [aNode.data] + self.makeList(aNode.lChild) + self.makeList(aNode.rChild)
def isSimilar(self, n, m):
nList = self.makeList(n.root)
mList = self.makeList(m.root)
print mList == nList
larsmans answered your first question
For your second question, can you simply look before you leap to avoid recursion?
def makeList(self, aNode=None):
if aNode is None:
aNode = self.root
treeaslist = [aNode.data]
if aNode.lChild:
treeaslist.extend(self.makeList(aNode.lChild))
if aNode.rChild:
treeaslist.extend(self.makeList(aNode.rChild))
return treeaslist
It doesn't work because default arguments are evaluated at function definition time, not at call time:
def f(lst = []):
lst.append(1)
return lst
print(f()) # prints [1]
print(f()) # prints [1, 1]
The common strategy is to use a None default parameter. If None is a valid value, use a singleton sentinel:
NOTHING = object()
def f(arg = NOTHING):
if arg is NOTHING:
# no argument
# etc.
If you want to treat None as a valid argument, you could use a **kwarg parameter.
def function(arg1, arg2, **kwargs):
kwargs.setdefault('arg3', default)
arg3 = kwargs['arg3']
# Continue with function
function("amazing", "fantastic") # uses default
function("foo", "bar", arg3=None) # Not default, but None
function("hello", "world", arg3="!!!")
I have also seen ... or some other singleton be used like this.
def function(arg1, arg2=...):
if arg2 is ...:
arg2 = default

Python: passing parameters over functions

Python Experts,
I have been trying to implement BST using Python and here is my code for the insert function:
Draft 1:
def insert(self, val):
newNode = self._Node(val)
if (self._root is None):
self._root = newNode
else:
self._insert(self._root,val)
def _insert(self, node, val):
if node is None:
node = self._Node(val)
elif val >= node._val:
self._insert(node._right, val)
else:
self._insert(node._left, val)
But, I'm unable to construct the tree except the root. I'm guessing I messed up somewhere with the parameters passing over the two functions because when I modify the code as below, I get it alright:
Draft 2:
def insert(self, val):
newNode = self._Node(val)
if (self._root is None):
self._root = newNode
else:
self._insert(self._root,val)
def _insert(self, node, val):
if val >= node._val:
if node._right is None:
node._right = self._Node(val)
else:
self._insert(node._right, val)
else:
if node._left is None:
node._left = self._Node(val)
else:
self._insert(node._left, val)
I'm trying hard to understand why the draft 2 works but draft 1 doesn't. Any help here? Thanks in advance!
The fundamental misunderstanding you have is how variable assignment works and interacts with Python's evaluation strategy: call-by-sharing.
Essentially, in your first draft, when you do the following:
def _insert(self, node, val):
if node is None:
node = self._Node(val)
...
You are simply assigning to the name (variable) node the value of self._Node(val) but then when you leave the scope, the new object is destroyed! Even though node used to refer to the value that was passed in by the method call, simple assignment doesn't mutate the object that is referenced by the name, rather, it reassigns the name.
In your second draft, however:
def _insert(self, node, val):
if val >= node._val:
if node._right is None:
node._right = self._Node(val)
else:
self._insert(node._right, val)
You are mutating an object i.e.: `node._right = self._Node(val)
Here is a simple example that is hopefully illuminating:
>>> def only_assign(object):
... x = 3
... object = x
...
>>> def mutate(object):
... object.attribute = 3
...
>>> class A:
... pass
...
>>> a = A()
>>> a
<__main__.A object at 0x7f54c3e256a0>
>>> only_assign(a)
>>> a
<__main__.A object at 0x7f54c3e256a0>
>>> mutate(a)
>>> a.attribute
3
I believe this is due to the fact that by doing :
node = self._Node(val)
in the _insert function you are not changing the value of the left/right node but binding the name node to a new _Node object, thus letting the left/right node as None.
On your second draft you are effectively affecting a new object to left / right node.
Here's a simple example to illustrate what happens on your code.
Guess what the print(test) will display?
test = [5, 5, 5]
def function(list):
list[0] = 10
list = range(1, 3)
function(test)
print test
If you think it will display [1,2] you're wrong .. it will actually display [10, 5, 5] because when doing list = range(1, 3) we are binding the name list to another object and not changing the first object it was bound to (the one test is actually bound to)

Python - Why is my locally defined linked list updated when I call other functions that doesn't return anything [duplicate]

This question already has answers here:
Function changes list values and not variable values in Python [duplicate]
(7 answers)
Closed 7 years ago.
The code below imports a linked list from LinkedQfile and creates a list object with some node objects.
If I run this code the output from check_something() becomes CD .
I thought linked_list in check_something() would become a local object inside the function and since I'm not assigning whatever I'm returning to anything it wouldn't change, that is I would expect the output ABCD. This is obviously not the case so I'm wondering if someone could explain to me what is going on here?
If linked_list was a global variable I would expect this outcome, my guess is that the return statements in each function returns some information to the object but I have no idea how and why! (I got the code from a lecture note and it works just like I want it to, I just want to know why!)
from LinkedQFile import LinkedQ
def check_something(linked_list):
check_first_element(linked_list)
check_second_element(linked_list)
print(linked_list)
def check_first_element(linked_list):
word = linked_list.dequeue()
if word == "A":
return
def check_second_element(linked_list):
word = linked_list.dequeue()
if word == "B":
return
def main():
list = LinkedQ()
list.enqueue("A")
list.enqueue("B")
list.enqueue("C")
list.enqueue("D")
check_something(list)
main()
And if needed, the LinkedQFile:
class Node:
def __init__(self, x, next= None):
self._data = x
self._next = next
def getNext(self):
return self._next
def setNext(self, next):
self._next = next
def getValue(self):
return self._data
def setValue(self, data):
self._data = data
class LinkedQ:
def __init__(self):
self._first = None
self._last = None
self._length = 0
def __str__(self):
s = ""
p = self._first
while p != None:
s = s + str(p.getValue())
p = p.getNext()
return s
def enqueue(self, kort):
ny = Node(kort)
if self._first == None:
self._first = ny
else:
self._last = self._first
while self._last.getNext():
self._last = self._last.getNext()
self._last.setNext(ny)
self._length += 1
def dequeue(self):
data = self._first.getValue()
self._first = self._first.getNext()
self._length = self._length - 1
return data
You're right about linked_list being a local variable, but just because a variable is local doesn't mean it can't reference something that isn't. In order for it to do what you expected, it would need to copy your entire linked list every time you pass it to a function, which wouldn't make sense.
Here's a simple example that illustrates the idea of a shared object. In this example, an empty list is created and assigned to a. Then a is assigned to b. This does not copy the list. Instead, there is a single list, referenced by both a and b. When it is modified, through either a or b, both a and b reflect the change:
>>> a = []
>>> b = a
>>> a.append("x")
>>> a
['x']
>>> b
['x']
>>>
The same thing is happening with your class objects. In fact, your linked lists wouldn't work at all if it didn't.

Categories

Resources