Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Here is my code of serialize and de-serialize a binary tree, looking for advice. One issue in my mind which I cannot resolve is, I am using a global variable index, wondering if more elegant solutions in Python 2.7 -- since in Python there is no solution to pass a reference as Java/C++ did.

index = 0
class Node:
    def __init__(self, value, left, right):
        self.value = value
        self.left = left
        self.right = right
    @staticmethod
    def serialize(root, output):
        if not root:
            output.append('#')
            return
        output.append(root.value)
        Node.serialize(root.left, output)
        Node.serialize(root.right, output)
        return output
    @staticmethod
    def deserialize(source):
        global index
        if source[index] == '#':
            index += 1
            return None
        value = source[index]
        index += 1
        left = Node.deserialize(source)
        right = Node.deserialize(source)
        return Node(value, left, right)

if __name__ == "__main__":
    root = Node('a', Node('b', None, None), Node('c', Node('e', Node('g', None, None), None), Node('f', None, None)))
    result = []
    Node.serialize(root, result)
    print result
    new_root = Node.deserialize(result)
    result2 = []
    Node.serialize(new_root, result2)
    print result2
share|improve this question
4  
"since in Python there is no solution to pass a reference". Where did you read this ? – Dex' ter Dec 12 '16 at 8:30
1  
You can use class attribute for storing index that which is also sort of global variable, but at least it's stored under class namespace. – Alex Dec 12 '16 at 10:04
    
@Alex, sounds good idea. If you could add a reply, I will mark it as answer to benefit other people. Also wondering if possible to do BFS other than DFS for serialize/deserialize? – Lin Ma Dec 12 '16 at 17:58
    
@Dex'ter, could you show me how to pass int in Python by reference? Tried to search something failed. – Lin Ma Dec 13 '16 at 4:51

For starter, you don't need to use a global index. Python tuples make it easy to return several values at once, you can return the new index with the deserialized value:

    @staticmethod
    def deserialize(source):
        def _helper(index):
            if source[index] == '#':
                return None, index + 1

            value = source[index]
            left, index = _helper(index + 1)
            right, index = _helper(index)
            return Node(value, left, right), index
        return _helper(0)[0]

Now, what if I what to serialize the node Node('#', None, None)? I will get ['#', '#', '#'] as output that will get deserialized as None. Not that ideal. If you want to keep this intermediate format, you should at least allow the user to chose its sentinel value. Using a parameter with a default value in both serialize and `deserialize is a good option.

I would also improve the interface of your class: turning serialize into a method and deserialize into a classmethod. This allow for ease of use and easy subclassing:

class Node(object):
    ...

    def serialize(self, sentinel='#'):
        serial = [self.value]
        if self.left is None:
            serial.append(sentinel)
        else:
            serial.extend(self.left.serialize(sentinel))
        if self.right is None:
            serial.append(sentinel)
        else:
            serial.extend(self.right.serializr(sentinel))
        return serial

    @classmethod
    def deserialize(cls, source, sentinel='#'):
        def _helper(index):
            if source[index] == sentinel:
                return None, index + 1

            value = source[index]
            left, index = _helper(index + 1)
            right, index = _helper(index)
            return cls(value, left, right), index
        return _helper(0)[0]

And at the very last, I would provide default values for the left and right parameters:

class Node(object):
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

So I can more easily write trees:

t = Node(13, Node(8), Node(15, right=Node(18)))
t.serialize()
# [13, 8, '#', '#', 15, '#', 18, '#', '#']

An other way to look at the problem is to take inspiration from repr. If we were to write an official representation for such objects, we could end up with:

    def __repr__(self):
        return '{}(value={!r}, left={!r}, right={!r})'.format(self.__class__.__name__, self.value, self.left, self.right)

And repr(t) would be the string:

Node(value=13, left=Node(value=8, left=None, right=None), right=Node(value=15, left=None, right=Node(value=18, left=None, right=None)))

It's a bit verbose, but it is serialized as a string. Using eval on this string would even deserialize it and return a Node equivalent to t. I however advise against the use of eval for deserialization purposes as it is too generic and thus dangerous.

Instead, serialization could return a more stripped form such as (8,,) for Node(8) or ('#',(5,,),) for Node('#', Node(5)) and deserialization would be a parser tailored for this representation.

Serialization is easy:

    def serialize(self):
        return '({!r},{},{})'.format(
            self.value,
            '' if self.left is None else repr(self.left),
            '' if self.right is None else repr(self.right))

Deserialization is left as an exercise for the reader.

share|improve this answer
    
Thanks Mathias, love your comments and vote up. Is there a solution using BFS (serialize tree layer by layer) other than DFS (or pre-order traverse as we did now) for serialize and de-serialize? – Lin Ma Dec 13 '16 at 4:50
    
BTW, also wondering if you have good ideas how store None as #? Suppose the tree is not very balanced, there will be a lot #. Correct? – Lin Ma Dec 13 '16 at 5:24

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.