[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