Role of "name" in Collection Property

Does anyone understand the proper use of the “name” field in Collection Properties?

Here’s an example from the 2.75 documentation: http://www.blender.org/api/blender_python_api_2_75_release/bpy.props.html :

import bpy


# Assign a collection
class SceneSettingItem(bpy.types.PropertyGroup):
    name = bpy.props.StringProperty(name="Test Prop", default="Unknown")
    value = bpy.props.IntProperty(name="Test Prop", default=22)

bpy.utils.register_class(SceneSettingItem)

bpy.types.Scene.my_settings = \
    bpy.props.CollectionProperty(type=SceneSettingItem)

# Assume an armature object selected
print("Adding 3 values!")

my_item = bpy.context.scene.my_settings.add()
my_item.name = "Spam"
my_item.value = 1000

my_item = bpy.context.scene.my_settings.add()
my_item.name = "Eggs"
my_item.value = 30

for my_item in bpy.context.scene.my_settings:
    print(my_item.name, my_item.value)

That code shows an explicit definition of a “name” property in the SceneSettingItem class.

However, it appears that a “name” field is inherited automatically from the PropertyGroup parent class.

To explore this, I created an addon that creates a collection of PropertyGroup elements without an explicit “name” property and a collection of PropertyGroup elements with an explicit “name” property. Here’s the code:

"""
This program explores the special use of the "name" field in property groups
"""

bl_info = {
  "version": "0.1",
  "name": "Name Collection Testing",
  'author': 'Bob',
  "location": "Properties > Scene",
  "category": "Blender Experiments"
  }


import bpy
from bpy.props import *


# An unnamed Element
class UElementPropertyGroup(bpy.types.PropertyGroup):
    s = StringProperty(default="UElement")

# A named Element
class NElementPropertyGroup(bpy.types.PropertyGroup):
    name = StringProperty()
    s = StringProperty(default="NElement")


class AppPropertyGroup(bpy.types.PropertyGroup):
    u_list = CollectionProperty(type=UElementPropertyGroup)
    n_list = CollectionProperty(type=NElementPropertyGroup)
    
    def fill_to ( self, num ):
        for i in range(num):

            e = self.u_list.add()
            e.name = "u" + str(i)
            e.s = e.name + "_string"

            e = self.n_list.add()
            e.name = "n" + str(i)
            e.s = e.name + "_string"

def register():
    print ("Registering ", __name__)
    bpy.utils.register_module(__name__)
    bpy.types.Scene.app = bpy.props.PointerProperty(type=AppPropertyGroup)

def unregister():
    print ("Unregistering ", __name__)
    del bpy.types.Scene.app
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()


You can invoke it from the Python console via: >>> C.scene.app.fill_to(3)

The two classes appear to function the same (although I haven’t done any performance testing yet). They both appear to have a “name” field, and they both appear to be indexable by the “name” field:

>>> C.scene.app.u_list[‘u2’].s
>>> C.scene.app.n_list[‘n2’].s

However, when I look at the data blocks, they differ. I find that they both have a “Name” field (yes, capital “N”), but the named version has an additional “name” field (lower case “n”). Changing the element.name value appears to change both fields in the Datablocks, and changing either field in the Datablocks appears to change the element.name value as well.

Here are my 2 questions:

  1. Should we define an explicit “name” property (as shown in the documentation example), or is it better to use the “name” that’s automatically generated in the class?

  2. Does an explicit “name” property improve or degrade performance?

FYI: Here’s a look at the Datablocks for this example from Blender 2.75. You’ll notice that the u0, u1, and u2 (unnamed) collection elements only have a “Name” field. However, the n0, n1, and n2 (named) collection elements have both a “Name” field and a “name” field.


I believe at least this one StringProperty is required, as it is used for certain things, like access by name instead of index:

bpy.types.Scene.p = bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)

item=C.scene.p.add()
item.name = "foo"

C.scene.p[0]
#~ bpy.data.scenes['Scene'].p[0]

C.scene.p[0].name
#~ 'foo'

C.scene.p["foo"]
#~ bpy.data.scenes['Scene'].p[0]

It will obviously return the first match only in case there are multiple items with the same name property, but if uniqueness is somehow ensured, it’s a nice way of accessing the collection members by name.

Layout.template_list() will display the name property by default (for “UI_UL_list”).

An explicit definition of the name property shouldn’t make a difference in performance, but it gives you the opportunity to configure the property (e.g. set description, subtype etc.)

It looks like the “name” is already there. Here’s what happens in the Console after a Factory Reset:

>>> bpy.types.Scene.p = bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)
>>> item = C.scene.p.add()
>>> dir(item)
['__doc__', '__module__', '__slots__', 'bl_rna', 'name', 'rna_type']

>>> 

In other words, the “name” field is already there before the assignment.

Looking at the Datablocks will show “p” with a “Name” field (capital N) that’s initially an empty string. But if the “name” is explicitly defined in the class declaration (as in the documentation example), then there will be two Datablock fields (one called “Name”, and the other “name”) as shown in the picture with red outlines. The two names (“Name” and “name”) will be somehow linked - changing one will change the other.

I would expect that to be true except that the different names (“Name” and “name”) show up as separate items in the Datablocks list (Outliner panel - see red-lined items in picture above). Is there some behind the scenes “magic” associated with the “name” field? Does creating an explicit “name” really create a separate data block or is that just a quirk of the Datablocks display in the Outliner?

This is probably the same as with any other property. It’s possible to re-define built-in properties, and it will appear as a single property, although there are apparently two like in your example. If you re-define Object.name, it’s possible to attach a callback to the new definition, but Blender will eventually crash. I would consider this a known limitation, possible memory leak.