Question

The main objective of the following module, is to provide a kind of "constant" semantics for some names.

class ConstantError(Exception):

    def __init__(self, msg):
            self._msg = msg


class Constant(object):

    def __init__(self, name):
            self._name = name

    def __get__(self, instance, owner):
            return instance._content[self._name]

    def __set__(self, instance, value):
            raise ConstantError, 'Illegal use of constant'


class Constants(object):

    def __init__(self, content):
            self._content = content
            for k in self._content:
                    setattr(self, k, Constant(k))

num_const = Constants({
    'one': 1,
    'two': 2
})

When used:

>>> from const import *
>>> dir(num_const)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', '_content', 'one', 'two']

So one and two are there, but the attribute access is diappointing:

>>> num_const.one
<const.Constant object at 0x7faef4871710>
>>> 

Where I wold expect 1 in this case. Where am I wrong?

Was it helpful?

Solution

The descriptor protocol only works on attributes of the class, not on attributes of instances of a class. See the How-To Guide for Descriptors

OTHER TIPS

You are missing a str() or unicode() method in Constants.

Add:

def __unicode__(self):
    return self._name

I think python prevents classes from accessing the descriptor machinery so that they can be manipulated. Otherwise manipulating the descriptor could get very tricky without some kind of 'magical' function, and if you've noticed python tries to keep a lot of the language machinery accessible. To get around this I've often generated the class on the fly. For example, you Constants class could be declared like so:

class Constants(object):
    def __new__(cls, content):
       class _Constants(object):
           pass
       constants = _Constants
       constants._content = content
       for k in constants._content:
            setattr(_Constants, k, Constant(k))
       return constants

but really, for you purposes you might be better of with:

class Constants(object):
    def __init__(self, content):
       self._content = content
    def __getattr__(self,key):
       return self._content[key]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top