#!/usr/bin/env PYTHONHASHSEED=1234 python3 # Copyright 2014-2019 Brett Slatkin, Pearson Education Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Reproduce book environment import random random.seed(1234) import logging from pprint import pprint from sys import stdout as STDOUT # Write all output to a temporary directory import atexit import gc import io import os import tempfile TEST_DIR = tempfile.TemporaryDirectory() atexit.register(TEST_DIR.cleanup) # Make sure Windows processes exit cleanly OLD_CWD = os.getcwd() atexit.register(lambda: os.chdir(OLD_CWD)) os.chdir(TEST_DIR.name) def close_open_files(): everything = gc.get_objects() for obj in everything: if isinstance(obj, io.IOBase): obj.close() atexit.register(close_open_files) # Example 1 class ToDictMixin: def to_dict(self): return self._traverse_dict(self.__dict__) # Example 2 def _traverse_dict(self, instance_dict): output = {} for key, value in instance_dict.items(): output[key] = self._traverse(key, value) return output def _traverse(self, key, value): if isinstance(value, ToDictMixin): return value.to_dict() elif isinstance(value, dict): return self._traverse_dict(value) elif isinstance(value, list): return [self._traverse(key, i) for i in value] elif hasattr(value, '__dict__'): return self._traverse_dict(value.__dict__) else: return value # Example 3 class BinaryTree(ToDictMixin): def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right # Example 4 tree = BinaryTree(10, left=BinaryTree(7, right=BinaryTree(9)), right=BinaryTree(13, left=BinaryTree(11))) orig_print = print print = pprint print(tree.to_dict()) print = orig_print # Example 5 class BinaryTreeWithParent(BinaryTree): def __init__(self, value, left=None, right=None, parent=None): super().__init__(value, left=left, right=right) self.parent = parent # Example 6 def _traverse(self, key, value): if (isinstance(value, BinaryTreeWithParent) and key == 'parent'): return value.value # Prevent cycles else: return super()._traverse(key, value) # Example 7 root = BinaryTreeWithParent(10) root.left = BinaryTreeWithParent(7, parent=root) root.left.right = BinaryTreeWithParent(9, parent=root.left) orig_print = print print = pprint print(root.to_dict()) print = orig_print # Example 8 class NamedSubTree(ToDictMixin): def __init__(self, name, tree_with_parent): self.name = name self.tree_with_parent = tree_with_parent my_tree = NamedSubTree('foobar', root.left.right) orig_print = print print = pprint print(my_tree.to_dict()) # No infinite loop print = orig_print # Example 9 import json class JsonMixin: @classmethod def from_json(cls, data): kwargs = json.loads(data) return cls(**kwargs) def to_json(self): return json.dumps(self.to_dict()) # Example 10 class DatacenterRack(ToDictMixin, JsonMixin): def __init__(self, switch=None, machines=None): self.switch = Switch(**switch) self.machines = [ Machine(**kwargs) for kwargs in machines] class Switch(ToDictMixin, JsonMixin): def __init__(self, ports=None, speed=None): self.ports = ports self.speed = speed class Machine(ToDictMixin, JsonMixin): def __init__(self, cores=None, ram=None, disk=None): self.cores = cores self.ram = ram self.disk = disk # Example 11 serialized = """{ "switch": {"ports": 5, "speed": 1e9}, "machines": [ {"cores": 8, "ram": 32e9, "disk": 5e12}, {"cores": 4, "ram": 16e9, "disk": 1e12}, {"cores": 2, "ram": 4e9, "disk": 500e9} ] }""" deserialized = DatacenterRack.from_json(serialized) roundtrip = deserialized.to_json() assert json.loads(serialized) == json.loads(roundtrip)