Search code examples
pythonpython-3.xmetaprogrammingstatic-variablesclass-variables

Python: Accessing static class variables from inside the class


So I'm trying to make a class that extends list, with the extra ability that certain special attributes are mapped to refer to certain parts of the list. Using this Py3k doc page, I created the following code. The idea is that (say I have a sequence instance of this class) sequence.seq should act exactly like sequence[0], and sequence.index should act exactly like sequence[2], etc.

It seems to work great, except that I can't seem to access the class variable mapping attributes to the list.

I found this SO question, but either the answer there is wrong, or something is different within methods. I could also use self.__class__.__map__, but since I need the class variable inside __getattribute__, that sends me into an infinite recursion loop.

>>> class Sequence(list):
...      __map__ = {'seq': 0,
...                 'size': 1,
...                 'index': 2,
...                 'fdbid': 3,
...                 'guide': 4,
...                 'factors': 5,
...                 'clas': 6,
...                 'sorttime': 7,
...                 'time': 8,
...                 'res': 9,
...                 'driver': 10 }
...      
...      def __setattr__(self, name, value): # "Black magic" meta programming to make certain attributes access the list
...           print('Setting atr', name, 'with val', value)
...           try:
...                self[__map__[name]] = value
...           except KeyError:
...                object.__setattr__(self, name, value)
...      
...      def __getattribute__(self, name):
...           print('Getting atr', name)
...           try:
...                return self[__map__[name]]
...           except KeyError:
...                return object.__getattribute__(self, name)
...      
...      def __init__(self, seq=0, size=0, index=0, fdbid=0, guide=None, factors=None, 
...           sorttime=None, time=None):
...                super().__init__([None for i in range(11)]) # Be sure the list has the necessary length
...                self.seq = seq
...                self.index = index
...                self.size = size
...                self.fdbid = fdbid
...                self.guide = ''
...                self.time = time
...                self.sorttime = sorttime
...                self.factors = factors
...                self.res = ''
...                self.driver = ''
... 
>>> a = Sequence()
Setting atr seq with val 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 31, in __init__
  File "<stdin>", line 17, in __setattr__
NameError: global name '__map__' is not defined

Solution

  • Since none of the methods are called until after Sequence is fully defined, you can refer to Sequence.__map__ without any trouble. For example:

    def __setattr(self, name, value):
        print('Setting atr', name, 'with val', value)
        try:
            self[Sequence.__map__[name]] = value
        except KeyError:
            object.__setattr__(self, name, value)
    

    As an aside, here's a demonstration that class attributes may be accessed via objects as long as an instance attribute with the same name does not also exist:

    class Foo:
        i = 3
        def __init__(self, overwrite):
            if overwrite:
                self.i = 4
    
    f = Foo(False)
    id(f.i) == id(Foo.i)     # Should be True
    f = Foo(True)
    id(f.i) == id(Foo.i)     # Should be False