Unexpected None in python print statement - python

I'm trying to print out a list of nodes connected to other nodes so I can view them for debugging some python code revolving around graphs. I'm doing this for practice so I'm setting up nodes/edges/groups myself.
I've tried printing out the connections with my function inside of the myNode class using a for each type loop. But I get different output if I specify the index of the array and call the same method on it.
def print_connections(self):
for node in self.connections:
print(node.name, end = " ")
...
for node in nodes:
node.print_connections()
print(" ") # so I can have a newline in between groups
print(nodes[1].print_connections())
The for/each has good output, or appears to be good:
2 5 3 0
which differs from the line with the indexed print:
2 5 3 0 None
Is this expected behavior in python? How can I get around this?

your print_connections(self) doesn't return any value, thus return None, and what you're trying to do is with print(nodes[1].print_connections()) is to print the returned value by nodes[1].print_connections(), which is going to be None,
so what you should do instead is just,
for node in nodes:
node.print_connections()
print(" ")
nodes[1].print_connections()

Related

The Foundry Nuke – Issues with `elif` and `else` statements

So to make it simple, I'm writing a script in NUKE which aligns the selected nodes in the node graph into one straight line in the Y-axis. I'm having issues where I'm writing the elif statement which is either not functioning as I want to or it is giving me a syntax error.
So the basis of the function is:
ELSE STATEMENT - when selected only one node - error message pops up saying user has to select more than one node
ELIF STATEMENT - when selected two or more nodes which are in the same Y-axis - message showing they are already aligned
IF STATEMENT - when selected two or more nodes in different Y-axis - it should properly align all the nodes in a straight line
# Getting selected nodes and making them into a list
selNodes = nuke.selectedNodes()
list = []
for node in selNodes:
n = node['ypos'].value()
list.append(n)
# Defining the actual function
def alignY():
# Aligning the selected nodes using average position of every node.
# Must select more than one node in order to get an average.
if len(selNodes) > 1:
total = sum(list)
average = total / len(selNodes)
for node in selNodes:
n = node['ypos'].setValue(average)
# Getting the position of a single node from the list
firstNodePostion = list[0]
# Checking position of the single node is equivalent to the average
# To prevent nodes aligning again)
elif average == firstNodePostion:
nuke.message("Nodes already Aligned")
# When no nodes or only one node is selected this message pops up
else:
nuke.message("Select Two or more Nodes")
alignY()
You have to indent the lines of you code according to Python rules.
So you need to use 4 spaces per indentation level – look at PEP 8.
import nuke
selNodes = nuke.selectedNodes()
list = []
for node in selNodes:
n = node['ypos'].value()
list.append(n)
def alignY():
if len(selNodes) > 1:
total = sum(list)
average = total / len(selNodes)
for node in selNodes:
n = node['ypos'].setValue(average)
firstNodePostion = list[0]
elif average == firstNodePostion:
nuke.message("Nodes already Aligned")
else:
nuke.message("Select Two or more Nodes")
alignY()
Now alignY() method works as expected.
Your problem is that you have a statement that is sitting between the if and the elif, which can cause a syntax error.
It's hard to tell though, because you have not provided the exact error message, but from a syntactical point, there shouldn't be another statement separating the if and the elif.

Removing a concrete value from a set using function

(Newbie question) I want to write a Python program that removes a concrete item from a set if it is present in the set.
When the set is predefined, the code goes like this:
set = {1,2,3,4,4,5,6,10}
set.discard(4)
print(set)
What would be a way to write this, so that it applies to any set of values not known beforehand? I tried the following but it didn´t work. Is there a method along those lines that does?
def set(items):
if i in items == 4:
set.discard(4)
else:
print("The number 4 is not in the set.")
print(set({1,2,4,6}))
This will discard the 4 in any set passed to the function:
def discard4(items):
if 4 in items:
items.discard(4)
return "Discarded"
else:
return "The number 4 is not in the set."
print(discard4({1,2,6})) # will print "The number 4 is not in the set."
print(discard4({1,2,4,6})) # will print "Discarded"

Maya Python skinCluster return type not string?

I'm trying to check if an object has a skinCluster on it. My code is pretty basic. Here's an example:
cmds.select(d=True)
joint = cmds.joint()
skinnedSphere = cmds.polySphere(r=2)
notSkinnedSphere = cmds.polySphere(r=2)
skinTestList = [skinnedSphere, notSkinnedSphere]
# Bind the joint chain that contains joint1 to pPlane1
# and assign a dropoff of 4.5 to all the joints
#
cmds.skinCluster( joint, skinnedSphere, dr=4.5)
for obj in skinTestList:
objHist = cmds.listHistory(obj, pdo=True)
skinCluster = cmds.ls(objHist, type="skinCluster")
if skinCluster == "":
print(obj + " has NO skinCluster, skipping.")
else:
print obj, skinCluster
#cmds.select(obj, d=True)
My issue is that even if it can't find a skincluster, it still prints out the "obj, skincluster" rather than the error that it can't find a skinCluster.
I thought a skinCluster returns a string. So if the string is empty, it should print out the error rather than "obj, skincluster".
Any help would be appreciated!
This is a classic Maya issue -- the problem is that Maya frequently wants to give you lists, not single items, even when you know the result ought to be a single item. This means you end up writing a bunch of code to either get one item from a one-item list or to avoid errors that come from trying to get an index into an empty list.
You've got the basics, it's the == "" which is messing you up:
for obj in skinTestList:
objHist = cmds.listHistory(obj, pdo=True)
skinCluster = cmds.ls(objHist, type="skinCluster") or [None]
cluster = skinCluster[0]
print obj, cluster
The or [None] guarantees that you'll always get a list with something in it so it's safe to use the [0] to get the single value. None is a good return value here because (as pointed out in the comments) you can if cluster: and skip empty values.

Perfect Binary Tree with correct data

I am having a problem trying to fill the data a perfect binary tree with a known number of nodes with the correct data. Basically, I have an implementation that creates this:
7
5 6
1 2 3 4
However, I am looking to create a tree like this:
7
3 6
1 2 4 5
My current implementation for inserting the nodes of a tree is as follows.
def _add_node(self, val, ref = None):
# reference to root of tree
ref = self.root if ref is None else ref
if ref.right is None:
ref.right = Node(val, ref)
return
elif ref.left is None:
ref.left = Node(val, ref)
return
else:
parent = (val - 1) / 2
if parent % 2 == 0:
self._add_node(val, ref.left)
else:
self._add_node(val, ref.right)
Given x nodes I create a tree using range(x) and calling add_node(i) for each iteration. This works fine except its order is incorrect.
For the life of me I cannot figure out an easy way to set the values to represent the bottom layout rather than the top. Can anyone help me out?
This seems to be an issue with the order that you are entering data in. How are you passing in the data?
Also think about your implementation. You check to see whether the right child is empty and if it is you place the node there. However, if it isn't you move on to the left node. This is where the issue is happening.
Assuming you are passing in the data in reverse chronological order you start with 7 at the root. Then you move to 6 which you place in the right node. Then move on to 5; you check to see whether the right node is empty, which is isn't because it is filled with 6, so you move on to check if the left node is empty and find that it is. So you place 5 there.
Do you see the issue?
You need to figure out a way to get around this issue, but hopefully this was good in helping you debug.
Good Luck!

Handling variable length command tuple with try...except

I'm writing a Python 3 script that does tabulation for forestry timber counts.
The workers will radio the species, diameter, and height in logs of each tree they mark to the computer operator. The computer operator will then enter a command such as this:
OAK 14 2
which signifies that the program should increment the count of Oak trees of fourteen inches in diameter and two logs in height.
However, the workers also sometimes call in more than one of the same type of tree at a time. So the program must also be able to handle this command:
OAK 16 1 2
which would signify that we're increasing the count by two.
The way I have the parser set up is thus:
key=cmdtup[0]+"_"+cmdtup[1]+"_"+cmdtup[2]
try:
trees[key]=int(trees[key])+int(cmdtup[3])
except KeyError:
trees[key]=int(cmdtup[3])
except IndexError:
trees[key]=int(trees[key])+1
If the program is commanded to store a tree it hasn't stored before, a KeyError will go off, and the handler will set the dict entry instead of increasing it. If the third parameter is omitted, an IndexError will be raised, and the handler will treat it as if the third parameter was 1.
Issues occur, however, if we're in both situations at once; the program hasn't heard of Oak trees yet, and the operator hasn't specified a count. KeyError goes off, but then generates an IndexError of its own, and Python doesn't like it when exceptions happen in exception handlers.
I suppose the easiest way would be to simply remove one or the other except and have its functionality be done in another way. I'd like to know if there's a more elegant, Pythonic way to do it, though. Is there?
I would do something like this:
def parse(cmd, trees):
res = cmd.split() # split the string by spaces, yielding a list of strings
if len(res) == 3: # if we got 3 parameters, set the fourth to 1
res.append(1)
for i in range(1,4): # convert parameters 1-3 to integers
res[i] = int(res[i])
key = tuple(res[x] for x in range(3)) # convert list to tuple, as lists cannot be dictionary indexes
trees[key] = trees.get(key,0) + res[3] # increase the number of entries, creating it if needed
trees={}
# test data
parse("OAK 14 2", trees)
parse("OAK 16 1 2", trees)
parse("OAK 14 2", trees)
parse("OAK 14 2", trees)
# print result
for tree in trees:
print(tree, "=", trees[tree])
yielding
('OAK', 16, 1) = 2
('OAK', 14, 2) = 3
Some notes:
no error handling here, you should handle the case when a value supposed to be a number isn't or the input is wrong in any other way
instead of strings, I use tuples as a dictionary index
You could use collections.Counter, which returns 0 rather than a KeyError if the key isn't in the dictionary.
Counter Documentation:
Counter objects have a dictionary interface except that they return a zero count for missing items instead of raising a KeyError
Something like this:
from collections import Counter
counts = Counter()
def update_counts(counts, cmd):
cmd_list = cmd.split()
if len(cmd_list) == 3:
tree = tuple(cmd_list)
n = 1
else:
*tree, n = tuple(cmd_list)
counts[tree] += n
Same notes apply as in uselpa's answer. Another nice thing with Counter is that if you want to, e.g., look at weekly counts, you just do something like sum(daily_counts).
Counter works even better if you're starting from a list of commands:
from collections import Counter
from itertools import repeat
raw_commands = get_commands() # perhaps read a file
command_lists = [c.split() for c in raw_commands]
counts = Counter(parse(command_lists))
def parse(commands):
for c in commands:
if len(c) == 3:
yield tuple(c)
elif len(c) == 4
yield from repeat(tuple(c[0:2]), times=c[3])
From there you can use the update_counts function above to add new trees, or you can start collecting the commands in another text file and then generate a second Counter object for the next day, the next week, etc.
In the end, the best way was to simply remove the IndexError handler, change cmdtup to a list, and insert the following:
if len(cmdtup) >= 3:
cmdtup.append(1)

Categories