3

I'm trying to set attributes to a class of which I don't know the name a-priori. I also want to avoid users to write to that attribute, so I use a property factory with getters and setters which returns a property object. However, when calling the property object, I get the reference to that object, instead of whatever the getter should be returning.

So I try to do this:

def property_factory(name):
    def getter(self):
        return self.__getattribute__(name)

    def setter(self, value):
        raise Exception('Cannot set a value')

    return property(getter, setter)

# This is just a read_file placeholder
class read_file(object):
    def __init__(self):
        self.name = 'myName'
        self.value = 'myValue'

    def __iter__(self):
        return self

class a(object):
    list1 = read_file()

    def __init__(self):

        list1 = read_file()

        self.__setattr__('_' + list1.name, list1.value)
        # this doesn't work:
        self.__setattr__(list1.name, property_factory('_' + list1.name))
        
        # this actually does work, but with the wrong attribute name:

    notMyName = property_factory('_' + list1.name)

Then I get this:

In [38]: b = a()

In [39]: b.myName
Out[39]: <property at 0x2883d454450>

In [40]: b.notMyName
Out[40]: 'myValue'

In [41]: b.notMyName = 'true'
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Cell In[41], line 1
----> 1 b.notMyName = 'true'

Cell In[37], line 6, in property_factory.<locals>.setter(self, value)
      5 def setter(self, value):
----> 6     raise Exception('Cannot set a value')

Exception: Cannot set a value

What I want is this:

In [39]: b.myName
Out[40]: 'myValue'

In [41]: b.MyName = 'true'
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Cell In[41], line 1
----> 1 b.MyName = 'true'

Cell In[37], line 6, in property_factory.<locals>.setter(self, value)
      5 def setter(self, value):
----> 6     raise Exception('Cannot set a value')

Exception: Cannot set a value

How do I do this?

Mezzerine
  • 33
  • 3
  • 1
    `property` must be attached to a class object, not to an instance. Try: `setattr(self.__class__, list1.name, property_factory('_' + list1.name))`. However, in this way all instances of `a` will have the same named properties. – ken May 26 '23 at 17:38
  • This actually also works indeed. Apply property to a class, need to remember that :). Thanks! – Mezzerine May 27 '23 at 14:41

1 Answers1

1

Why do you want to do this? I've always gone back to this answer whenever I have an idea that uses the notion of dynamically-named attributes -- which is essentially what you're trying to do here if I'm not mistaken (with added read-only "protection" applied only to the keys in list1). Do you need to use a property factory? You could do something like this:

class A(object):
    list1 = read_file()
    def __init__(self):
        self.__dict__[A.list1.name] = A.list1.value
    def __setattr__(self, name, value):
        if name == A.list1.name:
            raise Exception('Cannot set a value for this key!')

Now at least this works:

>>> b = A()
>>> b.myName
'myValue'
>>> b.myName = 'true'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __setattr__
Exception: Cannot set a value for this key!

However both methods will be susecptable to the following:

>>> b.__dict__['myName'] = 'true'
>>> b.myName
'true'

Obviously there's a lot of optimization to be done here, adding sentinels, name mangling, etc, plus I'd need a lot more information regarding ultimately what you're trying to achieve and why -- but is this getting a little closer to what you want? I'll delete this answer (or tidy) if necessary, too long for a comment. Also, typo:

In [41]: b.MyName = 'true'

Should be myName.

Eugene
  • 1,539
  • 12
  • 20
  • Thanks! The property factory was not a goal on its own, just my way of implementing write protection for stuff in list1. Your method is much cleaner, I like it. – Mezzerine May 26 '23 at 21:41