23

Suppose I have a class like this:

class Alphabet(object):
     __init__(self):
         self.__dict = {'a': 1, 'b': 2, ... 'z': 26}

     @property
     def a(self):
         return self.__dict['a']

     @property
     def b(self):
         return self.__dict['b']

     ...

     @property
     def z(self)
         return self.__dict['z']

This would be a long and cumbersome task to define and it seems highly redundant. Is there a way to dynamically create these properties? I know you can dynamically create attributes with the built-in setattr, but I want to have control over read/write/delete access (for this reason I want to use a property). Thanks!

3
  • 2
    doing @property\ndef something() is equivalent to something = property(something). Which is equivalent to doing (in this case) self.setattr('a', property(lambda self: self.__dict['a'])) Commented Jun 10, 2012 at 9:29
  • 2
    @JoelCornett: The second statement in your comment is incorrect, you need to set it on the class, not the instance: paste.aeum.net/show/106. The reason for this is because of how descriptors work: The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in an owner class (the descriptor must be in either the owner’s class dictionary or in the class dictionary for one of its parents). Commented Jun 10, 2012 at 9:35
  • @ThiefMaster: I stand corrected :) Commented Jun 10, 2012 at 9:38

2 Answers 2

45

Don't use properties but implement the following methods:

  • __getattr__(self, name)
  • __setattr__(self, name, value)
  • __delattr__(self, name)

See http://docs.python.org/reference/datamodel.html#customizing-attribute-access

Your __getattr__ method could look like this:

def __getattr__(self, name):
    try:
        return self.__dict[name]
    except KeyError:
        msg = "'{0}' object has no attribute '{1}'"
        raise AttributeError(msg.format(type(self).__name__, name))
Sign up to request clarification or add additional context in comments.

1 Comment

I guess, this loses the ability for IDEs to provide auto-complete attributes.
-2

Don't do that. Just let the consumers of the class at the __dict directly and trust them not to screw it up. Remember, we're all consenting adults here!

Ned Batchelder explains better than I can:


Keep data out of your variable names

The question reminded me of others I've seen on Stack Overflow or in the #python IRC channel:

  • How do I see if a variable exists?
  • How do I use a variable as the name of another variable?
  • How do I use a variable as part of a SQL table name?

The thing all these have in common is trying to bridge the gap between two domains: the data in your program, and the names of data in your program. Any time this happens, it's a clear sign that you need to move up a level in your data modeling. Instead of 26 lists, you need one dictionary. Instead of N tables, you should have one table, with one more column in it.

3 Comments

Ned has a good point, but there is a goal here of encapsulation. That's not just important for preventing mistakes (which consenting adults can make, too), but as a matter of convenience. The internal code of the class probably has a good reason to treat the dictionary as a dictionary while client code treats it as a set of attributes.
A problem with accessing __dict directly is it's ugly, especially with the name mangling that will be involved. A little syntactic sugar for clients goes a long way sometimes, not to mention the level of indirection it provides will facilitate possible future changes.
in regards to @martineau's comment, the name mangling would make this solution verry ugly, this is because the dict is prefixed with a double underscore. Also it is an unspoken rule that in python anything prefixed with an underscore should not be touched. Also if you use a property to define a class attribute then you don't want it do be modified.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.