Python: what's the pythonic way to perform this loop? - python

What is the pythonic way to perform this loop. I'm trying to pick a random key that will return a subtree and not the root. Hence: 'parent == None' cannot be true. Nor can 'isRoot==True' be true.
thekey = random.choice(tree.thedict.keys())
while (tree.thedict[thekey].parent == None)or(tree.thedict[thekey].isRoot == True):
thekey = random.choice(tree.thedict.keys())
.......
edit: it works now

key = random.choice([key for key, subtree in tree.thedict.items()
if subtree.parent and not subtree.isRoot])
(Corrected after comments and question edition)

get a random subtree that is not the
root
not_root_nodes = [key, node for key,node in tree.thedict.iteritems() if not ( node.parent is None or node.isRoot)]
item = random.choice( not_root_nodes )

I think that's a bit better:
theDict = tree.thedict
def getKey():
return random.choice(theDict.keys())
theKey = getKey()
while theDict[thekey].parent in (None, True):
thekey = getKey()
What do you think?

thekey = random.choice(tree.thedict.keys())
parent = thedict[thekey].parent
while parent is None or parent.isRoot:
thekey = random.choice(tree.thedict.keys())
parent = thedict[thekey].parent

thekey = random.choice(tree.thedict.keys())
parent = tree.thedict[thekey].parent
while parent is None or tree.thedict[thekey].isRoot:
thekey = random.choice(tree.thedict.keys())
parent = thedict[thekey].parent

I think your while condition is flawed:
I think you expect this: tree.thedict[thekey].parent == None
should be equal to this: tree.thedict[thekey].parent.isRoot == True
When in fact, for both to mean "this node is not the root", you should change the second statement to: tree.thedict[thekey].isRoot == True
As written, your conditional test says "while this node is the root OR this node's parent is the root". If your tree structure is a single root node with many leaf nodes, you should expect an infinite loop in this case.
Here's a rewrite:
thekey = random.choice(k for k in tree.thedict.keys() if not k.isRoot)

def is_root(v):
assert (v.parent != None) == (v.isRoot)
return v.isRoot
#note how dumb this function looks when you guarantee that assertion
def get_random_nonroot_key():
while True:
thekey = random.choice(tree.thedict.keys())
value = tree.thedict[thekey]
if not is_root(value): return key
or a refactoring of Roberto Bonvallet's answer
def get_random_nonroot_key():
eligible_keys = [k for k, v in tree.thedict.items() if not is_root(v)]
return random.choice(eligible_keys)

Personally, I don't like the repetition of initializing thekey before the while loop and then again inside the loop. It's a possible source of bugs; what happens if someone edits one of the two initializations and forgets to edit the other? Even if that never happens, anyone reading the code needs to check carefully to make sure both initializations match perfectly.
I would write it like so:
while True:
thekey = random.choice(tree.thedict.keys())
subtree = tree.thedict[thekey]
if subtree.parent is not None and not subtree.isRoot:
break
P.S. If you really just want the subtree, and don't care about the key needed to lookup the subtree, you could even do this:
while True:
subtree = random.choice(tree.thedict.values())
if subtree.parent is not None and not subtree.isRoot:
break
Some people may not like the use of "while True:" but that is the standard Python idiom for "loop forever until something runs break". IMHO this is simple, clear, idiomatic Python.
P.P.S. This code should really be wrapped in an if statement that checks that the tree has more than one node. If the tree only has a root node, this code would loop forever.

Related

Why does pathlib.Path equality operator return true for paths that resolve to the same string?

I'm trying to traverse up the directory tree like this:
from pathlib import Path
# ...
curr = Path(os.getcwd())
while True:
# do something with curr
if curr is curr.anchor:
break
curr = curr.parent
To my surprise, even though str(curr) == str(curr.anchor), both the expressions curr is curr.anchor and curr == curr.anchor return False, so I get an infinite loop.
Is the right way to fix this really changing the condition as follows:
if str(curr) == str(curr.anchor):
break
I'm curious to know why the pathlib.Path equality operator works this way, but I'm also open to more Pythonic solutions to the main problem.
curr is a Path. curr.anchor is a string. A string and a Path will not be equal, and will certainly not be the same object.

Python For loop display previous value if correct one match with XML

right now I have this XML
I need to get 1 value ("enabled") behind the correct one I get.
this is the code I'm using
def checktap(text):
nodes = minidom.parse("file.xml").getElementsByTagName("node")
for node in nodes:
if node.getAttribute("text") == text:
if node.getAttribute("enabled") == True:
return ("Yes")
else:
return ("No")
value = checktap("EU 39")
print(value)
with this code, I'll get the exact node I'm searching, and the value is enabled=True, but I need to get the one behind this (android.widget.LinearLayout) with the value enabled=False
You can use itertools.pairwise
from itertools import pairwise
def checktap(text):
nodes = minidom.parse("file.xml").getElementsByTagName("node")
for node1, node2 in pairwise(nodes):
if node2.getAttribute("text") == text and node2.getAttribute("enabled"):
return True
return False

BST remove function, trying to add odd and even numbers to determine which loop to enter

What I'm trying to do, Im not done with it yet, but I have this code right now:
def delete(self):
t=1
if isinstance(self.root,odd):
child=self.left
grandchild=child.right
if self.left == self.right==None:
return None
if self.left==None:
return self.right
if self.right==None:
return self.left
if grandchild:
while grandchild.right:
child = grandchild
grandchild = child.right
self.data = grandchild.data
child.right = grandchild.left
else:
self.left = child.left
self.data = child.data
return self
if isinstance(self.root,even):
Im not done with the code, but as for the assignment, Its supposed to do 2 alternatives (pretty much the same thing, but instead of going left, then right as far as possible, im gonna go right and then left as far as possible). What I'm thinking is to add t+=1 after each loop, and if the number is odd, I want it to enter the first loop, and if its even, I want it to enter the other loop (I haven't made that one yet). Can I do this easily (for I wanted to do, if isinstance(t,even) and if isinstance(t,odd) but python doesnt seem to have this syntax? Any ideas?
From what I understood, you want to figure whether t is odd or even.
You should use the % (Modulo) operator.
if t%2 == 0: # then it is even (divisible by 2)
if t%2 == 1: # it is odd (not divisible by 2)
I hope this answers your question.

Python: Recursive function for browsing all tree nodes

I'm trying to make an recursive function which finds all nodes in a tree. My function, let's name it child(), can find all children of the current node and returns a listĀ“of them.
global nodes
nodes = []
def func():
if len(child(c))==0:
return []
else:
for ch in child(c):
nodes.append(ch)
return func(ch)
It seems it does not work for some reason.
Do you have an idea whats wrong or the problem should be somewhere else in my code?
EDIT: The problem is probably
if len(child(c))==0:
return []
it should check another child instead of return []. But don't know what to put there.
it's because of the return statement inside the for, the function passed only on the first child in the list.
this should work
global nodes
nodes = []
def func(c):
for ch in child(c):
nodes.append(ch)
func(ch)

Recursion and return statements [duplicate]

This question already has answers here:
Why does my recursive function return None?
(4 answers)
Closed 8 months ago.
I'm fairly new to Python and recursive functions as a whole, so pardon my ignorance.
I am trying to implement a binary search tree in Python and have the following insert method (taken out of a class):
def insert(self, key, root=None):
'''Inserts a node in the tree'''
if root == None:
root = self.root
if root.key == None:
self._update(root, key)
return 0
else:
tmp = root
if key > tmp.key: # we work with the right subtree
self.insert(key, root=tmp.right)
elif key < tmp.key: # we work with the left subtree
self.insert(key, root=tmp.left)
else: # key already exists
return 0
I'm not sure if this is legible, but it traverses the tree until it gets to a None value and updates the node with the key to insert.
Now, the method works nicely and correctly creates a BST from scratch. But there's a problem with the return statements, as it only returns 0 if there is no recursion performed.
>>> bst.insert(10)
0
>>> bst.insert(15)
>>> bst.root.right.key
15
>>>
"Inserting" the root key again returns 0 (from line 15) the way it should.
>>> bst.insert(10)
0
I can't figure out why this happens. If I put a print statement in line 6, it executes correctly, yet it just won't return anything past the first insertion. Why is this? (I'm pretty sure I'm missing some basic information regarding Python and recursion)
Thanks for your help,
Ivan
P.S.: I've read that recursion is not the best way to implement a BST, so I'll look into other solutions, but I'd like to know the answer to this before moving on.
On your recursive lines, you do not return anything. If you want it to return 0, you should replace them with lines like:
return self.insert(key, root=tmp.left)
instead of just
self.insert(key, root=tmp.left)
You are inside a function and want to return a value, what do you do? You write
def function():
return value
In your case you want to return the value returned by a function call, so you have to do.
def function():
return another_function()
However you do
def function():
another_function()
Why do you think that should work? Of course you use recursion but in such a case you should remember the Zen of Python which simply says:
Special cases aren't special enough to break the rules.
You need a return statement in your recursive case. Try this adjustment.
def insert(self, key, root=None):
'''Inserts a node in the tree'''
if root == None:
root = self.root
if root.key == None:
self._update(root, key)
return 0
else:
tmp = root
if key > tmp.key: # we work with the right subtree
return self.insert(key, root=tmp.right)
elif key < tmp.key: # we work with the left subtree
return self.insert(key, root=tmp.left)
else: # key already exists
return 0

Categories

Resources