Use metaclass to allow forward declarations
<p>I want to do something decidedly unpythonic. I want to create a class that allows for forward declarations of its class attributes. (If you must know, I am trying to make some sweet syntax for parser combinators.)</p>

<p>This is the kind of thing I am trying to make:</p>

<pre><code>a = 1
class MyClass(MyBaseClass):
    b = a  # Refers to something outside the class
    c = d + b  # Here's a forward declaration to 'd'
    d = 1  # Declaration resolved
</code></pre>

<p>My current direction is to make a metaclass so that when <code>d</code> is not found I catch the <code>NameError</code> exception and return an instance of some dummy class I'll call <code>ForwardDeclaration</code>. I take some inspiration from <a href="https://bugs.python.org/issue26988" rel="nofollow"><code>AutoEnum</code></a>, which uses metaclass magic to declare enum values with bare identifiers and no assignment.</p>

<p>Below is what I have so far. The missing piece is: how do I continue normal name resolution and catch the <code>NameError</code>s:</p>

<pre><code>class MetaDict(dict):
    def __init__(self):
        self._forward_declarations = dict()
    def __getitem__(self,  key):
        try:
            ### WHAT DO I PUT HERE ??? ###
            # How do I continue name resolution to see if the
            # name already exists is the scope of the class
        except NameError:
            if key in self._forward_declarations:
                return self._forward_declarations[key]
            else:
                new_forward_declaration = ForwardDeclaration()
                self._forward_declarations[key] = new_forward_declaration
                return new_forward_declaration

class MyMeta(type):
    def __prepare__(mcs, name, bases):
        return MetaDict()

class MyBaseClass(metaclass=MyMeta):
    pass

class ForwardDeclaration:
    # Minimal behavior
    def __init__(self, value=0):
        self.value = value
    def __add__(self, other):
        return ForwardDeclaration(self.value + other)
</code></pre>

<p>To start with:</p>

<pre><code>    def __getitem__(self,  key):
        try:
            return super().__getitem__(key)
        except KeyError:
             ...
</code></pre>

<p>But that won't allow you to retrieve the global variables outside the class body. 
You can also use the <code>__missin__</code> method which is reserved exactly for subclasses of  dict:</p>

<pre><code>class MetaDict(dict):
    def __init__(self):
        self._forward_declarations = dict()

    # Just leave __getitem__ as it is on "dict"
    def __missing__(self,  key):
        if key in self._forward_declarations:
            return self._forward_declarations[key]
        else:
            new_forward_declaration = ForwardDeclaration()
            self._forward_declarations[key] = new_forward_declaration
            return new_forward_declaration
</code></pre>

<p>As you can see, that is not that "UnPythonic" - advanced Python stuff such as SymPy and SQLAlchemy have to resort to this kind of behavior to do their nice magic - just be sure to get it very well documented and tested.</p>

<p>Now, to allow for global (module) variables, you have a to get a little out of the way - and possibly somthing that may not be avaliablein all Python implementations - that is: introspecting the frame where the class body is to get its globals:</p>

<pre><code>import sys
...
class MetaDict(dict):
    def __init__(self):
        self._forward_declarations = dict()

    # Just leave __getitem__ as it is on "dict"
    def __missing__(self,  key):
        class_body_globals = sys._getframe().f_back.f_globals
        if key in class_body_globals:
             return class_body_globals[key]
        if key in self._forward_declarations:
            return self._forward_declarations[key]
        else:
            new_forward_declaration = ForwardDeclaration()
            self._forward_declarations[key] = new_forward_declaration
            return new_forward_declaration
</code></pre>

<p>Now that you are here - your special dictionaries are good enough to avoid NameErrors, but your <code>ForwardDeclaration</code> objects are far from smart enough -  when running:</p>

<pre><code>a = 1
class MyClass(MyBaseClass):
    b = a  # Refers to something outside the class
    c = d + b  # Here's a forward declaration to 'd'
    d = 1 
</code></pre>

<p>What happens is that <code>c</code> becomes a <code>ForwardDeclaration</code> object, but summed to the instant value of <code>d</code> which is zero. On the next line, <code>d</code> is simply overwritten with the value <code>1</code> and is no longer a lazy object. So you might just as well declare <code>c = 0 + b</code> . </p>

<p>To overcome this, <code>ForwardDeclaration</code>  has to be a class designed in a smartway, so that its values are always lazily evaluated, and it behaves as in the "reactive programing" approach: i.e.: updates to a value will  cascade updates into all other values that depend on it. I think giving you a full implementation of a working  "reactive" aware FOrwardDeclaration class falls off the scope of this question. - I have some toy code to do that on github at <a href="https://github.com/jsbueno/python-react" rel="nofollow">https://github.com/jsbueno/python-react</a> , though.</p>

<p>Even with a proper "Reactive"  ForwardDeclaration class, you have to fix your dictionary again so that the <code>d = 1</code> class works:</p>

<pre><code>class MetaDict(dict):
    def __init__(self):
        self._forward_declarations = dict()

    def __setitem__(self, key, value):
        if key in self._forward_declarations:
            self._forward_declations[key] = value
            # Trigger your reactive update here if your approach is not
            # automatic
            return None
         return super().__setitem__(key, value)
    def __missing__(self,  key):
        # as above
</code></pre>

<p>And finally, there is a way to avoid havign to implement a fully reactive aware class - you can resolve all pending FOrwardDependencies on the <code>__new__</code> method of the metaclass - (so that your ForwardDeclaration objects are manually "frozen" at class creation time, and no further worries - )</p>

<p>Something along:</p>

<pre><code>from functools import reduce

sentinel = object()
class ForwardDeclaration:
    # Minimal behavior
    def __init__(self, value=sentinel, dependencies=None):
        self.dependencies = dependencies or []
        self.value = value
    def __add__(self, other):
        if isinstance(other, ForwardDeclaration):
             return ForwardDeclaration(dependencies=self.dependencies + [self])
        return ForwardDeclaration(self.value + other)

class MyMeta(type):
    def __new__(metacls, name, bases, attrs):
         for key, value in list(attrs.items()):
              if not isinstance(value, ForwardDeclaration): continue
              if any(v.value is sentinel for v in value.dependencies): continue
              attrs[key] = reduce(lambda a, b: a + b.value, value.dependencies, 0) 

         return super().__new__(metacls, name, bases, attrs)
    def __prepare__(mcs, name, bases):
        return MetaDict()
</code></pre>

<p>And, depending on your class hierarchy and what exactly you are doing, rememebr to also update one class' dict <code>_forward_dependencies</code> with the <code>_forward_dependencies</code> created on its ancestors.
AND if you need any operator other than <code>+</code>, as you will have noted, you will have to keep information on the operator itself  - at this point, hou might as well jsut use  <code>sympy</code>. </p>

python python-3.x metaclass