4
$\begingroup$

Is it possible to set an attribute in a class dynamically? That is I don’t want to have the attribute (e.g. could be a property) defined in the body of the class directly, I want it to be defined later via setattr on user input.

# this is the callback from a UI EnumProperty 
def update_type(self, context):
      self.update_props()
      # the valueA attribute exists at this point
      print(“valueA = “, self.valueA)

def update_props(self):
    if hasattr(self, "valueA"):
        print("Deleting valueA prop")
        delattr(self, "valueA")
    else:
        # note: it appears that the valueA does not exist
        # even after a second call to this method.
        print("valueA prop not found. Create one.")
        setattr(self, “valueA”, 10)

for some reason (unclear to me at this point) even though the valueA gets set in the update_props() method and it’s visible its caller change_type, the next time the caller invokes the update_props() the valueA attribute does not exist anymore.

Note: this code was added to a blender add-on (sverchok) and I can’t guarantee that the above behavior is a blender or sverchok related at this point. If the above code is added to a simple class in python terminal for instance, it works fine and the attribute gets set permanently in the instance of the class.

$\endgroup$
  • $\begingroup$ What is type(self) in this context? $\endgroup$ – ideasman42 Apr 11 '17 at 3:02
  • $\begingroup$ type(self)= <class 'sverchok.nodes.number.mix_numbers.SvMixNumbersNode'> $\endgroup$ – DolphinDream Apr 11 '17 at 3:19
  • $\begingroup$ Why would you use setattr here in the first place? setattr(self, 'valueA', 10) is exactly the same as self.valueA = 10. $\endgroup$ – dr. Sybren Jun 9 '18 at 12:40
0
$\begingroup$

As this relates to Sverchok, but more broadly to bpy's PyNodes interface, i'll chime in with the limited knowledge / experience I have.

No you can not trust that variables/properties added to an instance of a class will "stick" to the class instance. You can also not remove a property at run-time. In this sense Node classes are not like regular Python classes. If you dynamically add a property inside a function it might exist for the duration of a function call, but once execution leaves that function then the variable/property ceases to be associated with the class instance. The evidence that this is the case is what probably triggered the question in the first place.

The only things you can trust are the Properties that are included in the Class definition. I suspect this makes node instances more of a "known quantity" and reduces complexity of the PyNode back-end.

We (sverchok) however do use a not widely documented/explained feature for storing arbitrary data (not bpy.props tho..at least I have not tried..) in a class instance. We call it the node_dict. When used correctly The node_dict (class member) gets a unique key per class instance, and we can pass anything into the value.

def some_random_function():
    ...



class SomeNode(...):
    node_dict = {}

    def sv_init(self):
        node_dict[hash(self)] = {}

    def store_data(self, data):
        storage = node_dict.get(hash(self))
        if storage:
            storage['some_function'] = some_random_function

    def process(self, ..):
        storage = node_dict.get(hash(self))
        if storage and storage.get('some_function'):
            storage['some_function']()  # call it.

This is about as minimal i can go to show some of the mechanism for dynamic data storage. There are examples of defining a node class dynamically (the monad Class relies on it) but it's something that I consider obfuscation and possibly a sign of placing importance on the wrong things.

Of course it would be awesome to have more fine grained control over the parameters of properties at runtime like (name, min, max....dynamic operator tooltip), without needing to dynamically define/register/unregister nodes if you want to change a single property-definition.

$\endgroup$
0
$\begingroup$

If you want to store custom properties on ID datablocks (so on Object, Mesh, Curve, etc.) you can use ID properties by accessing the datablock as a dictionary:

bpy.context.active_object['propertyname'] = 123

mesh = bpy.context.active_object.data
mesh['someprop'] = 'hello there'

You'll find those in the 'Custom Properties' panel in Blender's user interface too.

These ID properties are saved within the blend file, and thus survive saving & reloading the file.

$\endgroup$

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.