473,412 Members | 2,048 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,412 software developers and data experts.

Weaver/Yarn Pattern in Python

Hello everybody,

I am using Python to prototype a compression algorithm for tree-shaped
data, e.g., XML data or abstract syntax trees. I use a variant of the
visitor design pattern--called weaver/yarn pattern--in order to
traverse the tree that is being compressed or decompressed. I do not
know of any other interesting application of the weaver/yarn pattern,
but it seems like a very suitable subject of study for different
implementation strategies. Essentially, I am asking for input on my
design decisions.

First, let me give you a simple example of the traditional visitor
pattern. The following diagram illustrates a depth-first traversal of
a mini tree by a visitor. The basic idea is that the tree accepts a
visitor and helps it to do a kind of type-dispatch based on the kind
of nodes.
Tree data structure (nodes a,b,c of types A,B,C)

a:A
/ \
/ \
b:B c:C

Nested calls
------------
Call location Call stack

main: a.accept(v)
A: v.visitA(a)
V: b.accept(v)
B: v.visitB(b)
V: c.accept(v)
C: v.visitC(c)
class A(Node):
def accept(self, visitor):
visitor.visitA(self)
class B(Node):
def accept(self, visitor):
visitor.visitB(self)
class C(Node):
def accept(self, visitor):
visitor.visitC(self)
class Visitor:
"Perform specific tasks while visiting certain nodes"
def visitA(self, a):
for k in a.kids:
k.accept(self)
def visitB(self, b):
pass
def visitC(self, c):
pass

The advantage of this ping-pong between the tree and visitor v is that
v encapsulates related processing instructions. Several different
visitors can be maintained independently of each other and without
forcing changes to the tree node classes. The tree nodes only need to
provide a node.accept(visitor) method. Type-checking can ensure
the match between the visitor and the tree data structure.

Normally, visitors encapsulate different processing passes, which are
run one after the other, each time traversing the whole tree. I have
implemented the compression of trees as several (sub)visitors c1..cN
even though they could have been implemented as one big visitor.
Besides the easy recombinability of visitors this has the added
advantage that I can use the same visitors for compression and
decompression where this is appropriate.

But now I have a problem when decompressing. In order to run one
visitor after another the first one expects to traverse the whole
tree. But this is impossible in case of a decompressor. It lies in
the very nature of the application that the tree is being constructed
while the visitors work on it. Conceptually the solution is easy.
The decompression subvisitors d1..dM have to process the partially
available tree upto the point of traversal where it is available. At
each node execution has to iterate over the applicable code of d1..dM
in the given order. This necessitates a decomposition of visitors
into something that we call yarns and these yarns are weaved by one
visitor, which we call the weaver. Thus the name "Weaver/Yarn
Pattern" for this variation of the visitor pattern.

The following exemplifies my current implementation of the w/y pattern
for a recursive descent (ie depth-first traversal) visitor. For each
(sub)visitor d1..dM the former d<i>.visitX(x) method is divided into
several y.weaveX_...() methods. At entry and exit the weaver invokes
y.weaveX_First() and y.weaveX_Last(). Each descent into a child is
surrounded by y.weaveX_Before(kidno) and y.weaveX_After(kidno) method
calls.

class Yarn:
# methods replacing visitA(..)
def weaveA_First(self, a):
pass
def weaveA_Before(self, a, kidno):
pass
def weaveA_After(self, a, kidno):
pass
def weaveA_Last(self, a):
pass
# methods replacing visitB(..)
def weaveB_First(self, a):
pass
def weaveB_Last(self, a):
pass
# methods replacing visitC(..)
...
class Weaver:
"A special visitor which weaves yarns"
def __init__(self, yarns):
self.yarns = yarns
def visitA(self, a):
for y in self.yarns:
y.weaveA_First(a)
for i, k in enumerate(a.kids):
for y in self.yarns:
y.weaveA_Before(a, i)
k.accept(self)
for y in self.yarns:
y.weaveA_After(a, i)
for y in self.yarns:
y.weaveA_First(a)
def visitB(self, b):
for y in self.yarns:
y.weaveA_First(b)
for y in self.yarns:
y.weaveA_First(b)
def visitC(self, b):
...

By now it should be obvious that the boilerplate for this approach
becomes quite extensive and it would be desirable to reduce it. To
mitigate the problem I did three things:

- Automatically generate templates for yarn classes. The actual code
can be filled in. Disadvantage: No convenient way to deal with
changes to the tree data structure.

- The node.accept(weaver) methods are changed to call a "generic"
weaver.weave(node) method (instead of weaver.visitX(node)), which
hackishly constructs all the calls to the yarns by assembling the
method names from the __class__.__name__ and one of "First", "Last",
"Before", and "After".

This solution does pretty much what I want:

- Writing selected yarn methods allows me to express with little
overhead /what/ code to execute /when/ in the weaving process.

- Once the weaver/yarn interaction machinery is set up correctly, the
debugger points me to real code in case of bugs. This is an
advantage over approaches that create classes at runtime, e.g., use
of metaclasses or the "new" module.

OTOH, I'm not perfectly happy with this solution since it's totally
"hackish". For example, I have to use a function, hand-written
specifially for this purpose, in order to "type-check" the
correspondence of the tree types and the yarns.

Maybe Python is not the right language for an elegant solution and I
should look at more rigorously or differently typed languages, but I
thought it's worth presenting my case here and asking what other
python hackers think. Maybe there is another more pythonic way to do
this?

I looked into several of python's features, but to no avail.
Iterators don't seem to play well with yarns (especially if you try to
thread objects thru the yarns to support synthesized/inherited
attributes a known from attribute grammars). I also looked at
metaclasses in order to reduce the boilerplate but that did pay off
either.

There are certainly other languages and language concepts, which offer
interesting implementation alternatives:

- Dylan's generic functions seems to be a a good match for my problem,
which allows me to get around the method name mangling in
weaver.weave(node). Anybody an idea for a nice emulation of generic
functions in python? :)

- Lazy evaluation as provided by Haskell (or even Ocaml) seems to make
the traveral by several visitors unnecessary, but I'm not yet
convinced enough of this approach to start a full reimplementation.

- Systems for attribute grammar evaluation seem to address some of my
general concerns, but I am afraid that they might constrain me too
much.

If you're still reading this I'm impressed ;-) and I would be very
happy to hear what you think.

-Chris

--
Chris Stork <> Support eff.org! <> http://www.ics.uci.edu/~cstork/
OpenPGP fingerprint: B08B 602C C806 C492 D069 021E 41F3 8C8D 50F9 CA2F

Jul 18 '05 #1
1 2398
Christian Stork wrote:
Hello everybody,

I am using Python to prototype a compression algorithm for tree-shaped
data, e.g., XML data or abstract syntax trees. I use a variant of the
visitor design pattern--called weaver/yarn pattern--in order to
traverse the tree that is being compressed or decompressed. I do not
know of any other interesting application of the weaver/yarn pattern,
but it seems like a very suitable subject of study for different
implementation strategies. Essentially, I am asking for input on my
design decisions.

First, let me give you a simple example of the traditional visitor
pattern. The following diagram illustrates a depth-first traversal of
a mini tree by a visitor. The basic idea is that the tree accepts a
visitor and helps it to do a kind of type-dispatch based on the kind
of nodes.
....
The advantage of this ping-pong between the tree and visitor v is that
v encapsulates related processing instructions. Several different
visitors can be maintained independently of each other and without
forcing changes to the tree node classes. The tree nodes only need to
provide a node.accept(visitor) method. Type-checking can ensure
the match between the visitor and the tree data structure.

Normally, visitors encapsulate different processing passes, which are
run one after the other, each time traversing the whole tree. I have
implemented the compression of trees as several (sub)visitors c1..cN
even though they could have been implemented as one big visitor.
Besides the easy recombinability of visitors this has the added
advantage that I can use the same visitors for compression and
decompression where this is appropriate.

But now I have a problem when decompressing. In order to run one
visitor after another the first one expects to traverse the whole
tree. But this is impossible in case of a decompressor. It lies in
the very nature of the application that the tree is being constructed
while the visitors work on it. Conceptually the solution is easy.
The decompression subvisitors d1..dM have to process the partially
available tree upto the point of traversal where it is available. At
each node execution has to iterate over the applicable code of d1..dM
in the given order. This necessitates a decomposition of visitors
into something that we call yarns and these yarns are weaved by one
visitor, which we call the weaver. Thus the name "Weaver/Yarn
Pattern" for this variation of the visitor pattern.

The following exemplifies my current implementation of the w/y pattern
for a recursive descent (ie depth-first traversal) visitor. For each
(sub)visitor d1..dM the former d<i>.visitX(x) method is divided into
several y.weaveX_...() methods. At entry and exit the weaver invokes
y.weaveX_First() and y.weaveX_Last(). Each descent into a child is
surrounded by y.weaveX_Before(kidno) and y.weaveX_After(kidno) method
calls.
....
By now it should be obvious that the boilerplate for this approach
becomes quite extensive and it would be desirable to reduce it. To
mitigate the problem I did three things:

- Automatically generate templates for yarn classes. The actual code
can be filled in. Disadvantage: No convenient way to deal with
changes to the tree data structure.

- The node.accept(weaver) methods are changed to call a "generic"
weaver.weave(node) method (instead of weaver.visitX(node)), which
hackishly constructs all the calls to the yarns by assembling the
method names from the __class__.__name__ and one of "First", "Last",
"Before", and "After".

This solution does pretty much what I want:

- Writing selected yarn methods allows me to express with little
overhead /what/ code to execute /when/ in the weaving process.

- Once the weaver/yarn interaction machinery is set up correctly, the
debugger points me to real code in case of bugs. This is an
advantage over approaches that create classes at runtime, e.g., use
of metaclasses or the "new" module.

OTOH, I'm not perfectly happy with this solution since it's totally
"hackish". For example, I have to use a function, hand-written
specifially for this purpose, in order to "type-check" the
correspondence of the tree types and the yarns.

....

Have a look at aspect oriented programming:

http://www.ccs.neu.edu/home/lieber/AOP.html

In theory it sounds like a good match for what you need.

I don't know how well Python supports this, perhaps you
can use a metaclass for this, but I'm not sure.
Have fun,
Ype
--
email at xs4all.nl
Jul 18 '05 #2

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: BalyanM | last post by:
Hi, I am new to python.I am using it on redhat linux 9. I am interested to run python on a sun machine(SunE420R,os=solaris) with 4 cpu's for a pattern discovery/search program on biological...
176
by: Thomas Reichelt | last post by:
Moin, short question: is there any language combining the syntax, flexibility and great programming experience of Python with static typing? Is there a project to add static typing to Python? ...
9
by: Xah Lee | last post by:
# -*- coding: utf-8 -*- # Python # Matching string patterns # # Sometimes you want to know if a string is of # particular pattern. Let's say in your website # you have converted all images...
4
by: Xah Lee | last post by:
20050207 text pattern matching # -*- coding: utf-8 -*- # Python # suppose you want to replace all strings of the form # <img src="some.gif" width="30" height="20"> # to # <img...
6
by: Daniel Santa Cruz | last post by:
Hello all, I've been trying to go over my OO Patterns book, and I decided to try to implement them in Python this time around. I figured this would help me learn the language better. Well,...
5
by: pythoncurious | last post by:
Hi python experts In C++ I can do something like this: class Base { public: void f() { this->f_(); } private: virtual void f_() = 0; };
4
by: Anastasios Hatzis | last post by:
I'm looking for a pattern where different client implementations can use the same commands of some fictive tool ("foo") by accessing some kind of API. Actually I have the need for such pattern for...
4
by: dustin | last post by:
I've been hacking away on this PEP for a while, and there has been some related discussion on python-dev that went into the PEP: ...
0
by: rkmr.em | last post by:
the memory usage of a python app keeps growing in a x86 64 linux continuously, whereas in 32 bit linux this is not the case. Python version in both 32 bit and 64 bit linux - 2.6.24.4-64.fc8 Python...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.