First, I've encountered a confusing situation regarding property defaults and the use of getters. When I use a getter, I end up having to supply the default of the property a second time, even though the property has a default specified. Is this normal? Does a property's default attribute become obsolete once you define a getter for it?

Here is a simple situation to illustrate. The relevant lines are the getter() return value and the property default assignment:

            ('A', "A", "Info", global_flag_a ),
            ('B', "B", "Info", global_flag_b ),
            ('C', "C", "Info", global_flag_c ),
            ('D', "D", "Info", global_flag_d ),

class PropsPrefs(bpy.types.AddonPreferences):

    # getter
    def getter(self):

        # must supply defaults a second time here?
        return self.get( "enum_flag_var", global_flag_a | global_flag_b | global_flag_c )

    # setter
    def setter(self,value):

        # assign new value if not none
        if value != 0:
            self["enum_flag_var"] = value

    # enum flag variable
    enum_flag_var : EnumProperty(
        default={'A', 'B', 'C'}, # never gets used?

My second area of confusion is why we need to use self.get("property") instead of self.property (as well as self["property"]) in certain situations? I'm assuming it is because these properties are generated at run-time as they are written to for the first time?

Would anyone be able to elaborate on how this works, and when to use one syntax over the other? Any documentation about this behavior? My current (limited) strategy is to use self.property until it throws an error, then switch to self.get("property"). But because of my lack of understanding, I have no way of knowing which of these situations might throw an error under different circumstances.

  • 1
    $\begingroup$ It seem that the dictionary doesn't get the default values inserted when getter and setter are implemented (for any property type). The .get() is necessary because the dictionary doesn't (yet) contain the key, accessing it directly would result in a KeyError. $\endgroup$
    – Robert Gützkow
    Sep 17 '19 at 10:54
  • $\begingroup$ Thanks @rjg. Okay, that's what I was assuming. One reason I'm confused is because I typically only seem to have to use .get() in the class functions. I haven't needed it in other modules (yet). Is this because Blender does something to initialize the key at some point? Or does the key only get generated when it is written to? If anyone can help me understand which types of events generate the key, that would be extremely helpful. Thanks again $\endgroup$
    – Robert
    Sep 17 '19 at 11:53
  • 1
    $\begingroup$ The key is in the dictionary once the setter is executed. That would happen once the user interacts with the property in the UI or when you assign it in your script. In case it wasn't clear get() is commonly used with dictionaries because of they may not contain the key you searching for. It's not a special function that only exists in Blender. $\endgroup$
    – Robert Gützkow
    Sep 17 '19 at 11:55
  • $\begingroup$ That makes sense. So if you were writing an operator or function that made use of many user inputs, you would use .get() for all of them, just in case the user didn't modify them? If that's the case, it seems like .get() should pretty much always be used. Or at least at the top of functions/operators. That also means the default attribute for all properties just became obsolete. $\endgroup$
    – Robert
    Sep 17 '19 at 11:59
  • 1
    $\begingroup$ Yes that's probably a good idea. BTW the reason why self.property in the getter doesn't work is because accessing it like that also triggers the getter, which results in an infinite recursion. $\endgroup$
    – Robert Gützkow
    Sep 17 '19 at 12:04

The default value will not be automatically applied when declaring a property with a getter and setter.

In Python you can use the .get() function when accessing a key in a dictionary that may not exist. It allows you to specify a second argument which will be used as default value in case the key cannot be found. This is necessary for your use case, because the key "enum_flag_var" may not yet exists in the bpy_struct dictionary. Accessing it using self["enum_flag_var"] will result in a KeyError.

KeyError: 'bpy_struct[key]: key "enum_flag_var" not found'

The key will exist once you've assigned a value to the property in your script or when the user sets a value in the UI. You cannot use self.property in the getter, because that would in turn also call the getter and therefore cause an infinite recursion. That's why the value is accessed through the dictionary.

  • $\begingroup$ Thanks rjg. I wasn't able to find this information anywhere else. Maybe because I assumed it was a Blender concept when it was actually a Python concept. But I'm still assuming it will help a lot of new Blender developers. $\endgroup$
    – Robert
    Sep 17 '19 at 12:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.