| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """This module offers a serializer for ``MetaDict``. |
| 6 |
| 7 The ``MetaDictSerializer`` itself is a ``MetaDict``, only it is an ordered |
| 8 ``MetaDict`` (using an ``OrderedDict`` internally). |
| 9 |
| 10 Factory of metadict serializer is offered - ``GetSerializer``. |
| 11 What the factory does is it copies the structure of keys of a passed-in |
| 12 ``MetaObject`` and uses a ``key`` function to generate an ordered dict to |
| 13 maintain the key orders. |
| 14 |
| 15 Given the ``MetaDictSerializer`` we can serialize a |
| 16 ``MetaDict`` to a list of ``Element``s, and de-serialize a list of ``Element``s |
| 17 to a ``MetaDict``. |
| 18 |
| 19 Serialization is needed when we need an ordered traversal of a ``MetaDict``. |
| 20 De-Serialization is needed when we want to construct the list back to |
| 21 a ``MetaDict`` so we can have our customized grouping and keyword matching. |
| 22 |
| 23 An usecase in ``Predator`` is, in ``loglinear`` classification model, we use |
| 24 scipy to train ``meta_weight``(inherit from ``MetaDict``), the ``meta_weight`` |
| 25 has to be serialized to a list of floats for training, and after the training it |
| 26 should be de-serialized to a ``meta_weight`` to multiply with ``meta_feature``. |
| 27 """ |
| 28 |
| 29 from collections import OrderedDict |
| 30 |
| 31 from libs.meta_object import Element |
| 32 from libs.meta_object import MetaDict |
| 33 |
| 34 |
| 35 class ElementSerializer(Element): |
| 36 |
| 37 def ToList(self, element, default=None): |
| 38 """Serializes an ``Element`` to a list of this single element. |
| 39 |
| 40 Args: |
| 41 element (Element or None): The element to be serialized. If the element |
| 42 is None, return a list of one default value. |
| 43 default (Element or None) The default value to set when None element is |
| 44 provided. |
| 45 |
| 46 Returns: |
| 47 A list of single element. |
| 48 |
| 49 Raises: |
| 50 Exception: An error occurs when the passed-in meta object is not an |
| 51 ``Element`` object. |
| 52 """ |
| 53 if element is None: |
| 54 return [default] |
| 55 |
| 56 assert element.is_element, Exception( |
| 57 '%s can only serialize Element object.' % self.__class__.__name__) |
| 58 return [element] |
| 59 |
| 60 def FromList(self, element_list, constructor=None): |
| 61 """De-serializes from element_list to an ``Element``. |
| 62 |
| 63 Args: |
| 64 element_list (list of Element): A list of ``Element`` object. |
| 65 constructor (callable): contructor of ``Element`` class. |
| 66 |
| 67 Returns: |
| 68 The ``Element`` object in the element_list. |
| 69 |
| 70 Raises: |
| 71 Exception: An error occurs when the element_list is not 1. |
| 72 """ |
| 73 assert len(element_list) == len(self), Exception( |
| 74 'The element list should have the same length as serializer') |
| 75 |
| 76 constructor = constructor or (lambda x: x) |
| 77 return constructor(element_list[0]) |
| 78 |
| 79 def __len__(self): |
| 80 return 1 |
| 81 |
| 82 |
| 83 class MetaDictSerializer(MetaDict): |
| 84 """Class that serialize a ``MetaDict`` to a list of ``Element``s. |
| 85 |
| 86 This class itself is a ``MetaDict``, it has the same structure as the |
| 87 meta_dict it wants to serialize or de-serialize, and it is using a |
| 88 ``OrderedDict`` to keep track of the serializing order. |
| 89 """ |
| 90 |
| 91 def __init__(self, value): |
| 92 super(MetaDictSerializer, self).__init__(value) |
| 93 self._length = None |
| 94 |
| 95 def ToList(self, meta_dict, default=None): |
| 96 """Serializes a ``MetaDict`` to a list of ``Element``s. |
| 97 |
| 98 Args: |
| 99 meta_dict (MetaDict or None): The element to be serialized. If meta_dict |
| 100 is None, return a list of default values. |
| 101 default (Element or None) The default value to set when None meta_dict is |
| 102 provided. |
| 103 |
| 104 Returns: |
| 105 A list of ``Element``s. |
| 106 """ |
| 107 element_list = [] |
| 108 for key, serializer in self.iteritems(): |
| 109 sub_meta_dict = meta_dict.get(key) if meta_dict else None |
| 110 element_list.extend(serializer.ToList(sub_meta_dict, default=default)) |
| 111 |
| 112 return element_list |
| 113 |
| 114 def FromList(self, element_list, |
| 115 meta_constructor=None, element_constructor=None): |
| 116 """De-serializes from element_list to an ``MetaDict``. |
| 117 |
| 118 Args: |
| 119 element_list (list of Element): A list of ``Element`` object. |
| 120 element_constructor (callable): The constructor of ``Element`` object that |
| 121 takes one value in element_list as input. |
| 122 meta_constructor (callable): The contructor of ``MetaDict`` object that |
| 123 only take one dict of MetaObjects as input. |
| 124 |
| 125 Returns: |
| 126 The ``MetaDict`` object contructed from element_list. |
| 127 |
| 128 Raises: |
| 129 Exception: An error occurs when the length of element_list is not equal |
| 130 to the serializer length. |
| 131 """ |
| 132 assert len(self) == len(element_list), Exception( |
| 133 'The element list should have the same length as serializer') |
| 134 |
| 135 meta_constructor = meta_constructor or (lambda x: x) |
| 136 meta_objs = {} |
| 137 index = 0 |
| 138 for key, serializer in self.iteritems(): |
| 139 # Truncate the segment in the element list to construct |
| 140 # the ``MetaObject`` corresponding to ``key``. |
| 141 segment = element_list[index : (index + len(serializer))] |
| 142 if serializer.is_element: |
| 143 meta_objs[key] = serializer.FromList(segment, element_constructor) |
| 144 else: |
| 145 meta_objs[key] = serializer.FromList(segment, meta_constructor, |
| 146 element_constructor) |
| 147 |
| 148 index += len(serializer) |
| 149 |
| 150 return meta_constructor(meta_objs) |
| 151 |
| 152 def _Length(self): |
| 153 """Methods to get the length of the serializer recusively. |
| 154 |
| 155 Note, the length of a serializer is the number of elements, which is also |
| 156 the number of "real values" in a ``MetaDict`` structure. |
| 157 """ |
| 158 if self._length is not None: |
| 159 return self._length |
| 160 |
| 161 self._length = 0 |
| 162 for value in self.itervalues(): |
| 163 self._length += len(value) |
| 164 |
| 165 return self._length |
| 166 |
| 167 def __len__(self): |
| 168 return self._Length() |
| 169 |
| 170 |
| 171 def GetSerializer(meta_object, key=None): |
| 172 """Factory to get a serializer corresponding to a ``MetaObject``. |
| 173 |
| 174 Args: |
| 175 meta_object (MetaObject): ``Element`` or ``MetaDict`` objects. |
| 176 key (callable or None): Key function to sort ``MetaDict`` object. |
| 177 """ |
| 178 if meta_object.is_element: |
| 179 return ElementSerializer() |
| 180 |
| 181 sorted_meta = sorted(meta_object.iteritems(), key=key) |
| 182 ordered_dict = OrderedDict((key, GetSerializer(sub_meta)) |
| 183 for key, sub_meta in sorted_meta) |
| 184 |
| 185 return MetaDictSerializer(ordered_dict) |
| OLD | NEW |