Как проверить, не имеет ли свойство узла значение None при итерации по материалам?

Aug 17 2020

Я сталкиваюсь с ошибкой отсутствия атрибута при итерации по узлам и группам узлов в некоторых смешанных файлах.

Ошибка: 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':

Ответы

3 batFINGER Aug 17 2020 at 17:41

Атрибут может быть 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и пустой список[]

Такие методы, как hasattrreturn или 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")
0451 Aug 17 2020 at 04:14

На основе комментария Рона Дженсенса:

    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':