マテリアルの反復中にノードプロパティがNoneでないかどうかを確認するにはどうすればよいですか?

Aug 17 2020

一部のブレンドファイルでノードとノードグループを反復処理しているときに、属性なしエラーが発生しました。

エラー:AttributeError: 'NoneType'オブジェクトに属性 'name'がありません

オブジェクトに「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は、blenderスクリプトで見られる一般的なエラーです。たとえば、次のような場合があります。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'
        ):

andsの連鎖が最初に停止するので False

使用する getattr

get属性を使用して3番目の引数をデフォルトとして使用すると、どちらもほぼ同じように扱われるためです。

node_tree = getattr(node, "node_tree", None)

なりNone、両方のケースで。IMOは、hasノードツリーとname属性testの両方を使用して、この「切り札」を作成します。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いずれTrueかを返すなどのメソッドFalse

if hasattr(foo, "bar") == True:

foo.bar存在する場合、これはやや似ています

if True == True:

代わりに単に使用する

if hasattr(foo, "bar"):

ノードにnode_tree属性がない場合に、noではなくブールfalseを返すように少し上を変更if nt:すると、どちらの場合もfalseになりますが、2つを区別できます。

        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

Ron Jensensのコメントに基づく:

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