On Mon, Nov 23, 2020 at 8:20 AM Brian Coleman <brianfcole...@gmail.com> 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 chris.bar...@noaa.gov
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/S6Y24VDUD5RL2SDU3LE56P3AONSAGMGM/ Code of Conduct: http://python.org/psf/codeofconduct/