0

My goal is to create a function that will procedurally generate a series of other functions within a class from serialized data. This is easy enough using dict , but... i would like for each function to be initialized with the @property decorator (or a similar custom decorator) so that i can call these functions like attributes

Basically, I would like to do something like the following:

class myClass(object):
    def __init__(self):
        self.makeFuncs(['edgar','allan','poe'])

    def makeFuncs(self, data):
        for item in data:
            self.__dict__[item] = '[%s] <--- is_the_data' % item 

myInstance = myClass()
print myInstance.poe
#'[poe] <--- is_the_data'

Got any Ideas?

Tony Cronin
  • 1,623
  • 1
  • 24
  • 30
Pax
  • 237
  • 3
  • 11
  • I don't understand what your question is. Your code already does what you say you want to do, and you're not creating any functions there at all. In what way are functions and/or properties involved in what you want? – BrenBarn Mar 08 '14 at 07:41
  • right now in order to get the proper value returned i need to use myInstance.poe() I am simply hoping to use myInstance.poe as you would with @property – Pax Mar 08 '14 at 07:44
  • Your code as written returns the value with `myInstance.poe`. No functions are involved in the access of the `poe` data. In what way do you want to involve functions? – BrenBarn Mar 08 '14 at 07:45
  • I think using the word "function" may have been a misnomer. \I am just trying to avoid using brackets () – Pax Mar 08 '14 at 07:47
  • as you would when using the @property decorator – Pax Mar 08 '14 at 07:47
  • and thanks for the fast reply.. amazing,, – Pax Mar 08 '14 at 07:48
  • Again, I don't understand what you mean. In the code you have written, it has `myInstance.poe` without parentheses. It works and gives the value you want. What is the problem? – BrenBarn Mar 08 '14 at 07:48
  • ok you are right. one sec I will re-post the code. This was an over simplified example – Pax Mar 08 '14 at 07:51

3 Answers3

1

You can dynamically add propertys, but properties are added to the class object, not the instance.

Here's an example:

def make_prop(cls, p):
    def f(self):
        print 'IN %s' % p
        return '[%s]' % p
    return f    

class myClass(object):
  pass

# add the properties
for p in ('edgar','allan','poe'):
    setattr(myClass, p, property(make_prop(myClass, p)))

y = myClass()
print y.a
print y.b

Prints:

IN allan
[allan]
IN poe
[poe]

Also, it is essential to use make_prop to create the function object, instead of creating them directly inside the for loop, due to python's lexical scoping. I.e. this won't work as expected:

# add the properties
for p in ('edgar','allan','poe'):
    def f(self):
        print 'IN %s' % p
        return '[%s]' % p
    setattr(myClass, p, property(f))
shx2
  • 61,779
  • 13
  • 130
  • 153
  • wait.. is 'make_prop' supposed to be a function within 'myClass'? and is 'cls' supposed to be 'self'? – Pax Mar 08 '14 at 08:11
  • glad I could help! feel free to upvote and/or accept an helpful answer – shx2 Mar 08 '14 at 08:35
  • i cant... tis my first post.. I haven't the rep.. but when I do, I will shower you in up votes, I promise – Pax Mar 08 '14 at 08:43
1

Here is the answer I came to for procedurally adding properties to a custom shader class in maya.

Thx @shx2 !

import maya.cmds as mc
import sushi.maya.node.dependNode as dep

class Shader(dep.DependNode):
    def __init__(self, *args, **kwargs):
        super(Shader, self).__init__(*args, **kwargs)
        makeProps(self.__class__, ['color','transparency','ambientColor','incandescence','diffuse','translucence','translucenceDepth','translucenceFocus'])

def createShaderProperties(attrName):
    def getterProp(self):
        return mc.getAttr('%s.%s' % (self.name, attrName))[0]
    def setterProp(self, value):
        mc.setAttr('%s.%s' % (self.name, attrName), *value, type = 'double3')
    return (getterProp, setterProp)

def makeProps(cls, data):
    for dat in data:
        getterProp, setterProp = createShaderProperties(dat)
        setattr(cls, dat, property(getterProp))
        setattr(cls, dat, property.setter(cls.__dict__[dat],setterProp))
Pax
  • 237
  • 3
  • 11
0

Your current idea won't work because property objects need to be in the class in order to work (they are descriptors). Since your list of functions is specific to each instance, that won't be possible.

However, you can make the general idea work using __getattr__. Here's an implementation that I think does what you want given a dictionary mapping from names to functions:

class MyClass(object):
    def __init__(self, func_dict):
        self.func_dict = func_dict

    def __getattr__(self, name):
        if name in self.func_dict:
            return self.func_dict[name]()     # call the function

        raise AttributeError("{} object has no attribute named {}"
                             .format(self.__class__.__name__, name)) # or raise an error
Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • Holy S#$i$%. this is my first post. you guys solved my problem in 30 seconds flat.. amazing. thank you. – Pax Mar 08 '14 at 07:55
  • hmmm... actually.. nope.. this wont work, because func's in the "func_dict" do not have access to "self" in the class instance unless maybe functools?..... seems messy though – Pax Mar 08 '14 at 08:17
  • if that wasn't clear... I am trying to define the function within the context of the class , or the instance... If that makes sense. – Pax Mar 08 '14 at 08:20
  • @Pax: You could pass the functions `self` when you call them from `__getattr__` if you want. If necessary, you could pass other arguments too, though I'm not sure how likely that would be to make sense. – Blckknght Mar 08 '14 at 08:38