3

I have an object that describes a dataset with multiple required pieces of data and metadata. The different data and metadata elements are themselves objects of the same type with a number of different attributes, including a name and a value attribute. Most of the interactions with these data elements are with the value attribute and it has become annoying to constantly reference it using .value, so I am trying to define a property for each data element, but I want to do it programmatically so when I add a new data element it automatically gets a property defined for it. To be specific, here is some example code:

class my_par(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

class my_obj(object):    
    def __init__(self, xval, yval, zval):
        self.xval = my_par('x', xval)
        self.yval = my_par('y', yval)
        self.zval = my_par('z', zval)

I can make properties on the my_obj class using

@property
def x(self):
    return self.xval.value

@x.setter
def x(self, value):
    self.xval.value = value

but I want a way to do it programmatically over all the my_par type objects. (I already have an iterator over those objects, so all I need is the code to define properties programmatically).

Bryna H
  • 41
  • 4
  • 1
    One related and quite famous SO page is [How to add property to a python class dynamically?](http://stackoverflow.com/questions/1325673/how-to-add-property-to-a-python-class-dynamically) and another with an interesting answer from @AlexMartelli is [Dynamically adding \@property in python](http://stackoverflow.com/questions/2954331/dynamically-adding-property-in-python). – Dilettant Jun 16 '16 at 21:12
  • 1
    I found those pages in trying to figure this out and they helped but they didn't do exactly what I wanted, which was to define the properties in the `__init__` so they existed for all instances. – Bryna H Jun 16 '16 at 21:16

3 Answers3

1

In python, there are functions getattr and setattr that can get and set properties of objects. It's used like this:

setattr(obj, "prop", value) # set the value
getattr(obj, "prop") # get the value
Michael Zhang
  • 1,445
  • 12
  • 14
1

After trying a lot of things that didn't work, this did:

class my_par(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

class my_obj(object):    
    def __init__(self, xval, yval, zval):
        self.xval = my_par('x', xval)
        self.yval = my_par('y', yval)
        self.zval = my_par('z', zval)

        par_list = ['xval', 'yval','zval']

        for p in par_list:
            this_par = getattr(self, p)
            attr_name = this_par.name
            setattr(self.__class__, attr_name, property(self.prop_fget(p), self.prop_fset(p)))


    def prop_fget(self, attr_name):
        def fget(self):
            this_par = getattr(self, attr_name)
            return this_par.value
        return fget


    def prop_fset(self, attr_name):
        def fset(self, value):
            this_par = getattr(self, attr_name)
            this_par.value = value
            setattr(self, attr_name, this_par)
        return fset    

Note in particular the use of self.__class__ in setattr in order to attach the property to the class rather than the instance.

Bryna H
  • 41
  • 4
  • You could post that over at codereview.stackexchange.com to see if anyone can help you tighten it up at all. – rrauenza Jun 16 '16 at 21:59
0

Try namedtuple

from collections import namedtuple

my_par = namedtuple('par', 'name value')
par = my_par('par_name', 'par_value')
print(par[0]) #outputs: par_name
print(par.value) #outputs: par_value
name, value = par
print(name,value) #outputs: par_name par_value
Gábor Fekete
  • 1,343
  • 8
  • 16