# Python: selecting node inputs which have the same name

For example the Mixed Shader node has 3 inputs, with names "Fac", "Shader" and "Shader".

Note that 2 of the inputs have the same name; but the first has an "identifier" of "Shader" and the second is "Shader_001".

How can I select the second input (without using it's numerical index).

So this works to select the first input:

material = bpy.data.objects["Cube"].active_material


But this does not work to select the second input:

material = bpy.data.objects["Cube"].active_material


This works, but I DO NOT want to do this as I may not always know the correct index number:

material = bpy.data.objects["Cube"].active_material
material.node_tree.nodes[0].inputs[2].name


Note that I've added a work around by defining by own function below; but this feels like it must already existing within the blend libraries?

def getByIdentifier(inputs, id):
for x in inputs:
if x.identifier == id:
return x

material = bpy.data.objects["Cube"].active_material
$$$$

• Very interesting question. I feel like it's a pretty nice workaround for the time being. By the way, if you're open to constructive critisicism, according to python guidelines, you should write method names in snake_case (pep 8) and you should not overwrite the builtin id (could lead to unexpected results if testing for equality) Jul 6 '21 at 18:16
• getByIdentifier(inputs, "Shader_001") can be replaced with a oneliner next((i for i in inputs if i.identifier == "Shader_001"), None) Jul 7 '21 at 12:18

Sometimes what we DON'T WANT is the go.

Have previously commented on questions / answers where the poster was indexing by number that indexing by name was "better" more readable etc. Then around about the time of this answer

Principled BSDF via Python API

I discovered that changing the blender language, also changed the socket names. (This appears to be no longer the case, notice though changing to Espanol has changed the node name)

>>> mix = D.materials['Material'].node_tree.nodes['Mezclar sombreadores']
>>> mix


I DO NOT want to do this as I may not always know the correct index number:

Once we have a reference to the node, the indices of the inputs. These are always in the same order.

>>> for i, s in enumerate(mix.inputs.values()):
...     i, s.name, s.identifier
...
(0, 'Fac', 'Fac')


The inputs collection, like so many collections in blender, including making our own via bpy.props.CollectionProperty is a built-in extension type. Such a collection can be indexed like a list or a dictionary, however unlike a dictionary items can have same name.

>>> type(mix.inputs)
<class 'bpy_prop_collection'>

>>> type(D.objects)
<class 'bpy_prop_collection'>


Some workarounds

Further to question method to search on the identifier, could:

Rename it

>>> mix.inputs[2].name = "Bob"
>>> mix.inputs.get("Bob")


Create script to iterate over all nodes and change the socket names.

The name will remain as "Bob" for the blender session. It will revert back to "Shader" for new mix node or on save / re-open.

Kind of silly on one hand, on the other shows how simply one can totally stuff up any script that relies on object names

(0, 'Fac', 'Fac')


... notice this has also auto renamed the read-only identifier property.

Monkey Patch

An example of monkey-patching a method onto a blender class.

This is using not the index in the inputs, rather the index 1 to signify this is the 2nd input socket with the same name. In hindsight perhaps returning the list, instead of passing the index.

Could instead use identifier here, but as noted above there is no guarantee.

import bpy
from collections import defaultdict

context = bpy.context

def wireup(name):
def getinput(self):
coll = getattr(self, name, {})
def _get(name, index=0, default=None):
sockets = defaultdict(list)
for k, v in coll.items():
sockets[k].append(v)
ret =  sockets[name][index]
return ret if ret else default
return _get
return getinput

bpy.types.Node.getinput = property(wireup("inputs"))
bpy.types.Node.getoutput = property(wireup("outputs"))


Test in console.

>>> mix.getinput("Shader")



Other suggestions would be a simple node group to wrap the mix node with io names of our choosing, or enumerating the input and output socket names to a panel in the node editor.

To sum up, in as much as can workaround, perhaps the "goto" method here is to use the index directly, ie that second shader input in a mix node is always in slot 2, and reflect the choice via comment or variable name.

# Mix Node
#    Inputs
#    (0, 'Fac')
`