[Cython] Visitor patterns and Python 2.4

Dag Sverre Seljebotn dagss at student.matnat.uio.no
Fri May 2 13:38:53 CEST 2008


Are there any real reasons for leaving the Cython compiler (not talking 
about generated or supported code of course) at Python 2.3, rather than 
a small bump to 2.4? Reason: I'd like decorators.

The rationale: Notice that parse tree visitors can currently be written 
like this:

class AnalyseControlFlow(VisitorTransform):
    def pre_FuncDefNode(self, node):
        node.body.analyse_control_flow(node.scope)
        return False # do not recurse beyond first function level


The VisitorTransform parent implements a tree iteration/recursion and 
calls "pre_X" which is only allowed to visit and stop recursion at that 
point, "process_X" which has full control (can replace or remove node, 
do various infix processing -- if "process_X" is implemented, pre and 
post are not called) and "post_X" (which only visits)).

VisitorTransform basically looks up node.__class__.__name__ to figure 
out the name of the function to call, and uses introspection. Things are 
cached.

There are two disadvantages to this though:
- Relying on node.__class__.__name__ is a bit fragile. Multiple; and 
simply changing a class. It would be better to use a real reference

- If using multiple instances of a Transform class, each instance have 
to rebuild their call table cache. (This is not a real issue though, I 
don't expect such cases to come up.) Using a metaclass, the call table 
could however be done at class definition time which is "nicer". This is 
not a good reason.

If bumping the version to 2.4, decorators can solve this. OTOH, I'd much 
rather do with function names rather than "manually calling decorators". 
The example above could then look like this:

class AnalyseControlFlow(VisitorTransform):
    @pre(FuncDefNode)
    def call_analyse_control_flow(self, node):
        node.body.analyse_control_flow(node.scope)
        return False # do not recurse beyond first function level

This has the advantage that the class is directly references rather than 
being embedded in a string, so that name clashes, class renaming etc 
works correctly. Like this:

from Nodes import FuncDefNode
funcdef = FuncDefNode

class AnalyseControlFlow(VisitorTransform):
    @pre(funcdef)
    def call_analyse_control_flow(self, node):
        ...

Note that the standard visitor pattern (implementing accept on each and 
every node) was shot down by Fabrizio earlier (in private communication) 
as wasting code lines and unpythonic, and I guess I agree, so I 
implemented his suggestion instead. (The only reason I can see is speed 
when running under Cython compilation, if one uses "cdef" and 
compilation binding so that a dictionary lookups happen anyway. Not the 
thing to worry about at this stage IMO.)

-- 
Dag Sverre



More information about the Cython-dev mailing list