Pythonic way to dynamically add properties and setters

David picture David · Apr 10, 2015 · Viewed 10.8k times · Source

I'm working on a library for a 3D application that wraps the application's functionality into a set of Python classes, as well as adding additional functionality that the application doesn't have.

I have a set of properties in my base class to add attributes to a node using the existing API, but I'd like a more elegant way of adding attributes dynamically. I currently have this code (which probably won't make much sense without familiarity with the 3D application) but the important bit is the

# Add block properties

and

# PROPERTIES

code.

class Block(pyunify.Transform):
    def __init__(self, name = "Character"):
        pyunify.Transform.__init__(self, name)
        self.LockTransform()


class Spine(Block):
    def __init__(self, name = "Character", numberOfJoints=6):
        Block.__init__(self, name+"_spine")

        # Add block properties
        self.AddAttr("numberOfJoints", "long")
        self.SetAttr("numberOfJoints", numberOfJoints)

        # Create block template
        self.Template()

    # PROPERTIES
    @property
    def numberOfJoints(self):
        return self.GetAttr("numberOfJoints")

    @numberOfJoints.setter
    def numberOfJoints(self, num):
        self.SetAttr("numberOfJoints", num)

So I could foresee additional classes having a lot more properties, and the way I have it now, for every property I have to add it in _init__

self.AddAttr("numberOfJoints", "long")
self.SetAttr("numberOfJoints", numberOfJoints)

and then I have to add 2 additional functions in my class, a getter and setter, for that property so that I can call it within my class as self.numberOfJoints and it will interact with the class itself as well as setting the correct attributes on the accompanying node in the 3D application.

    @property
    def numberOfJoints(self):
        return self.GetAttr("numberOfJoints")

    @numberOfJoints.setter
    def numberOfJoints(self, num):
        self.SetAttr("numberOfJoints", num)

The only difference with the additional property functions will be the name, so I was wondering if there was a way I could create an AddProperty function in my base Block class so that I could just do this in my _init__ function:

self.AddProperty("numberOfJoints", "long")
self.numberOfJoints = 5

and it will dynamically create the getter and setter functions in addition to running the current _init__ line of

self.AddAttr("numberOfJoints", "long")

Any help would be appreciated!

Thanks!

Answer

theodox picture theodox · Apr 11, 2015

If you want to do something that looks like property access, but under the performs a function, you want a descriptor. A descriptor is an object that looks like a property but intercepts get and set calls and lets you run whatever code you need. Here's a stackoverflow question with an example that uses descriptors to change an object's translation using descriptors:

https://stackoverflow.com/a/22292961/1936075

You could use the same trick to create, get and set custom attributes in a more readable, less cmds-heavy fashion.

More code here.