Как проверить, не имеет ли свойство узла значение None при итерации по материалам?
Я сталкиваюсь с ошибкой отсутствия атрибута при итерации по узлам и группам узлов в некоторых смешанных файлах.
Ошибка: AttributeError: объект «NoneType» не имеет атрибута «имя»
Как мне проверить, нет ли у объекта свойства name, и пропустить его?
for mat in bpy.data.materials:
if mat.use_nodes == True:
for node in mat.node_tree.nodes:
#check if no has attribute name
if node.type in ["GROUP"] and node.node_tree.name == 'NodeGroupName':
Ответы
Атрибут может быть None
Ошибка NoneType has no attribute
является распространенной ошибкой в сценариях блендера. Например, это может быть случай, когдаcontext.object is None
OP рекомендовал бы отредактировать заголовок вопроса, чтобы более точно отразить это сообщение об ошибке.
Error: AttributeError: 'NoneType' object has no attribute 'name'
брошенный node.node_tree.name == 'NodeGroupName'
информирующий node.node_tree
имеет значение None
. Можно проверить, так ли это, т. Е.
if node.node_tree is None:
в отличие от
if hasattr(node, "node_tree"):
Оба будут верны, когда дерево узлов None
На самом деле это странный случай, когда рассматриваемый код вызывает эту ошибку, поскольку я (пока) не знаю, как добавить узел группы и не иметь связанного node_tree. Предположите, что это результат удаления группы узлов, bpy.data.node_groups
которая была узлом дерева узлов материала.
следовательно, последняя строка сценария вопроса может быть
if (node.type in ["GROUP"]
and node.node_tree
and node.node_tree.name == 'NodeGroupName'
):
так как цепочка и прекращается сначала False
С использованием getattr
Поскольку мы в значительной степени относимся к одному и тому же, используя атрибут get с третьим аргументом по умолчанию
node_tree = getattr(node, "node_tree", None)
будет None
в обоих случаях. ИМО, это «козыри» с использованием как дерева узлов, так и теста атрибута имени. None
не имеет ничего, если мы знаем, что дальнейшее тестирование атрибутов не покажется излишним. Также можно гарантировать, что если дерево узлов не является деревом узлов, у None
него будет имя .bpy.types.ShaderNodeTree.name
Тестовый сценарий
Используя вышеизложенное с "двойным тестом" для узла, 'GROUP'
имеющего проверку типа, он не имеет дерева узлов, отличного от нулевого.
Сделайте словарь с ключами, соответствующими имени группы узлов, содержащим список названий материалов, кортежей имен узлов.
import bpy
from bpy import data
from collections import defaultdict
# all the group nodes in blendfile
print(data.node_groups.keys())
group_nodes = defaultdict(list)
# create a look up dictionary
for m in data.materials:
if m.use_nodes:
for n in m.node_tree.nodes:
nt = getattr(n, "node_tree", None)
if nt and n.type == 'GROUP':
group_nodes[nt.name].append((m.name, n.name))
print(group_nodes)
Выход.
Файл с 1 группой узлов «Test_Material», 2 материалами «TestMat» и «TestMat.001».
Группа узлов используется в обоих материалах с именем "Группа" в обоих.
['Test_Material']
defaultdict(<class 'list'>, {'Test_Material': [('TestMat', 'Group'), ('TestMat.001', 'Group')]})
Заметки:
Остерегайтесь приравнивания к тестам True
или False
в тестах. По умолчанию python приравнивает большинство типов к True или false. Некоторые примеры false: None
целое число 0
и пустой список[]
Такие методы, как hasattr
return или True
илиFalse
if hasattr(foo, "bar") == True:
если foo.bar
существует, это читается как нечто похожее на
if True == True:
вместо этого просто используйте
if hasattr(foo, "bar"):
Слегка изменив приведенное выше значение, чтобы вернуть логическое значение false вместо none, когда узел не имеет атрибута node_tree, if nt:
в обоих случаях будет false, но мы можем различить между ними.
nt = getattr(n, "node_tree", False)
if nt and n.type == 'GROUP':
group_nodes[nt.name].append((m.name, n.name))
if nt is None:
print(f"{m.name}:{n.name} node_tree is None")
На основе комментария Рона Дженсенса:
for mat in bpy.data.materials:
if mat.use_nodes == True:
for node in mat.node_tree.nodes:
if hasattr(node, 'node_tree') == True:
if hasattr(node.node_tree, 'name') == True:
if node.type in ["GROUP"] and node.node_tree.name == 'NodeGroupName':