On Mon, Nov 23, 2020 at 8:20 AM Brian Coleman <[email protected]>
wrote:
> Take as an example a function designed to process a tree of nodes similar
> to that which might be output by a JSON parser. There are 4 types of node:
>
> - A node representing JSON strings
> - A node representing JSON numbers
> - A node representing JSON arrays
> - A node representing JSON dictionaries
>
> The function transforms a tree of nodes, beginning at the root node, and
> proceeding recursively through each child node in turn. The result is a
> Python object, with the following transformation applied to each node type:
>
> - A JSON string `->` Python `str`
> - A JSON number `->` Python `float`
> - A JSON array `->` Python `list`
> - A JSON dictionary `->` Python `dict`
>
> I have implemented this function using 3 different approaches:
>
> - The visitor pattern
> - `isinstance` checks against the node type
> - Pattern matching
>
I've always thought that the alternative to a "switch case" construct in
Python (and I suppose most OO languages) is subclassing and method
overriding. I guess that's what the "visitor pattern" is here, but it seems
to be adding a bunch of unnecessary bolierplate.
So: given that you have a special "Node" object anyway, the thing to do is
to have those Node object know how to unpack themselves. Then the top
"traverse the tree" function becomes a single method or attribute access:
tree = node_tree.value
here's what the Nodes look like in this example:
class Node:
def __init__(self, val):
self._value = val
@property
def value(self):
return self._value
class StringNode(Node):
pass
class NumberNode(Node):
pass
class ListNode(Node):
@property
def value(self):
return [item.value for item in self._value]
class DictNode(Node):
@property
def value(self):
return {k: item.value for k, item in self._value.items()}
Of course, this requires that you have control of the Node objects, rather
than getting them from some other library -- but that seems to be what all
the examples here are anyway.
If you do need to parse out a tree of object that are not "special"
already, then you need to do some type of pattern matching / isinstance
checking. In this case, I wrote a function that builds up a tree of Nodes
from arbitrary Python objects:
def make_nodes_from_obj(obj):
if isinstance(obj, str):
return StringNode(obj)
if isinstance(obj, Real):
return NumberNode(obj)
if isinstance(obj, Sequence):
return ListNode([make_nodes_from_obj(item) for item in obj])
if isinstance(obj, Mapping):
return DictNode({k: make_nodes_from_obj(item)
for k, item in obj.items()})
And that could benefit from pattern matching, I suppose, though it's not
very compelling to me.
And in "real world" code, I've done just this -- building a system for
saving / restoring dataclasses to/from JSON. In that case, each of the
dataclasses knows how to save itself and build itself from JSON-compatible
python objects (numbers, dicts, strings, lists) -- so again, no need for
pattern matching there either. And what I really like about the approach of
putting all the logic in the "nodes" is that I can make new types of nodes
without having to touch the code at the "top" that visits those nodes.
In short -- I'm still looking for a more compelling example :-)
-CHB
--
Christopher Barker, Ph.D.
Oceanographer
Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception
[email protected]
_______________________________________________
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/S6Y24VDUD5RL2SDU3LE56P3AONSAGMGM/
Code of Conduct: http://python.org/psf/codeofconduct/