1# Copyright 2020-2021 Huawei Technologies Co., Ltd 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================ 15"""container""" 16from __future__ import absolute_import 17 18from collections import OrderedDict, abc 19from abc import abstractmethod 20 21from mindspore.nn.cell import Cell 22 23__all__ = ['SequentialCell', 'CellList', 'CellDict'] 24 25 26def _valid_index(cell_num, index, op_name=None): 27 """Internal function, used to detect the value and type of index.""" 28 msg_prefix = f"For '{op_name}', the" if op_name else "The" 29 if not isinstance(index, int): 30 raise TypeError(f"{msg_prefix} type of 'index' must be int, but got {type(index).__name__}.") 31 if not -cell_num <= index < cell_num: 32 raise IndexError(f"{msg_prefix} value of 'index' must be a number in range [{-cell_num}, {cell_num}), " 33 f"but got {index}.") 34 return index % cell_num 35 36 37def _valid_index_for_inserting(cell_num, index, op_name=None): 38 """ 39 Internal function, used to detect the value and type of index for inserting Cell in 40 SequentialCell or CellList. 41 """ 42 msg_prefix = f"For '{op_name}', the" if op_name else "The" 43 if not isinstance(index, int): 44 raise TypeError(f"{msg_prefix} type of 'index' must be int, but got {type(index).__name__}.") 45 if not -cell_num <= index <= cell_num: 46 raise IndexError(f"{msg_prefix} value of 'index' must be a number in range [{-cell_num}, {cell_num}], " 47 f"but got {index}.") 48 return index % cell_num if (cell_num != 0 and index != cell_num) else index 49 50 51def _valid_cell(cell, op_name=None): 52 """Internal function, used to check whether the input cell is a subclass of Cell.""" 53 if issubclass(cell.__class__, Cell): 54 return True 55 msg_prefix = f"For '{op_name}'," if op_name else "" 56 raise TypeError(f'{msg_prefix} each cell must be subclass of Cell, but got {type(cell).__name__}.') 57 58 59def _get_prefix_and_index(cells): 60 """get prefix and index of parameter name in sequential cell or cell list.""" 61 prefix = "" 62 index = 0 63 if not cells: 64 return prefix, index 65 66 cell_list = list(cells.items()) 67 first_param, first_key = None, None 68 second_param, second_key = None, None 69 for key, cell in cell_list: 70 try: 71 _, param = next(cell.parameters_and_names()) 72 except StopIteration: 73 continue 74 if first_param is None: 75 first_param = param 76 first_key = key 77 continue 78 second_param = param 79 second_key = key 80 break 81 82 if first_param is None: 83 return prefix, index 84 85 split_names = first_param.name.split(".") 86 for idx, name in enumerate(split_names): 87 if name == first_key: 88 prefix = ".".join(split_names[:idx]) 89 prefix = prefix + "." if prefix else prefix 90 index = idx 91 if second_param is not None and second_param.name.split(".")[idx] == second_key: 92 break 93 return prefix, index 94 95 96class _CellListBase: 97 """ 98 An interface for base the Cell as list. 99 100 The sequential Cell may be iterated using the construct method using for-in statement. 101 But there are some scenarios that the construct method built-in does not fit. 102 For convenience, we provide an interface that indicates the sequential 103 Cell may be interpreted as list of Cells, so it can be accessed using 104 iterator or subscript when a sequential Cell instantiate is accessed 105 by iterator or subscript, it will be interpreted as a list of Cells. 106 """ 107 def __init__(self): 108 """Initialize _CellListBase.""" 109 self.__cell_as_list__ = True 110 111 @abstractmethod 112 def __len__(self): 113 pass 114 115 @abstractmethod 116 def __getitem__(self, index): 117 pass 118 119 def construct(self): 120 raise NotImplementedError 121 122 123class SequentialCell(Cell): 124 """ 125 Sequential Cell container. For more details about Cell, please refer to 126 `Cell <https://www.mindspore.cn/docs/en/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell>`_. 127 128 A list of Cells will be added to it in the order they are passed in the constructor. 129 Alternatively, an ordered dict of cells can also be passed in. 130 131 Args: 132 args (list, OrderedDict): List or OrderedDict of subclass of Cell. 133 134 Inputs: 135 - **x** (Tensor) - Tensor with shape according to the first Cell in the sequence. 136 137 Outputs: 138 Tensor, the output Tensor with shape depending on the input `x` and defined sequence of Cells. 139 140 Raises: 141 TypeError: If the type of the `args` is not list or OrderedDict. 142 143 Supported Platforms: 144 ``Ascend`` ``GPU`` ``CPU`` 145 146 Examples: 147 >>> import mindspore 148 >>> from mindspore import Tensor, nn 149 >>> import numpy as np 150 >>> 151 >>> conv = nn.Conv2d(3, 2, 3, pad_mode='valid', weight_init="ones") 152 >>> relu = nn.ReLU() 153 >>> seq = nn.SequentialCell([conv, relu]) 154 >>> x = Tensor(np.ones([1, 3, 4, 4]), dtype = mindspore.float32) 155 >>> output = seq(x) 156 >>> print(output) 157 [[[[27. 27.] 158 [27. 27.]] 159 [[27. 27.] 160 [27. 27.]]]] 161 >>> from collections import OrderedDict 162 >>> d = OrderedDict() 163 >>> d["conv"] = conv 164 >>> d["relu"] = relu 165 >>> seq = nn.SequentialCell(d) 166 >>> x = Tensor(np.ones([1, 3, 4, 4]), dtype=mindspore.float32) 167 >>> output = seq(x) 168 >>> print(output) 169 [[[[27. 27.] 170 [27. 27.]] 171 [[27. 27.] 172 [27. 27.]]]] 173 """ 174 def __init__(self, *args): 175 """Initialize SequentialCell.""" 176 super(SequentialCell, self).__init__() 177 self._is_dynamic_name = [] 178 if len(args) == 1: 179 cells = args[0] 180 if isinstance(cells, Cell): 181 cell = cells 182 self.insert_child_to_cell(str(0), cell) 183 cell.update_parameters_name(str(0) + ".") 184 self._is_dynamic_name.append(True) 185 elif isinstance(cells, list): 186 for index, cell in enumerate(cells): 187 self.insert_child_to_cell(str(index), cell) 188 cell.update_parameters_name(str(index) + ".") 189 self._is_dynamic_name.append(True) 190 elif isinstance(cells, OrderedDict): 191 for name, cell in cells.items(): 192 self.insert_child_to_cell(name, cell) 193 cell.update_parameters_name(name + ".") 194 self._is_dynamic_name.append(False) 195 else: 196 raise TypeError(f"For '{self.__class__.__name__}', the 'args[0]' must be Cell, list or orderedDict, " 197 f"but got {type(cells).__name__}") 198 else: 199 for index, cell in enumerate(args): 200 self.insert_child_to_cell(str(index), cell) 201 cell.update_parameters_name(str(index) + ".") 202 self._is_dynamic_name.append(True) 203 self.cell_list = list(self._cells.values()) 204 205 def __getitem__(self, index): 206 if isinstance(index, slice): 207 return self.__class__( 208 OrderedDict(list(self._cells.items())[index])) 209 index = _valid_index(len(self), index, self.__class__.__name__) 210 return list(self._cells.values())[index] 211 212 def __setitem__(self, index, cell): 213 cls_name = self.__class__.__name__ 214 if _valid_cell(cell, cls_name): 215 prefix, _ = _get_prefix_and_index(self._cells) 216 index = _valid_index(len(self), index, cls_name) 217 key = list(self._cells.keys())[index] 218 self._cells[key] = cell 219 cell.update_parameters_name(prefix + key + ".") 220 self.cell_list = list(self._cells.values()) 221 222 def __delitem__(self, index): 223 cls_name = self.__class__.__name__ 224 if isinstance(index, int): 225 index = _valid_index(len(self), index, cls_name) 226 key = list(self._cells.keys())[index] 227 del self._cells[key] 228 del self._is_dynamic_name[index] 229 elif isinstance(index, slice): 230 keys = list(self._cells.keys())[index] 231 for key in keys: 232 del self._cells[key] 233 del self._is_dynamic_name[index] 234 else: 235 raise TypeError(f"For '{cls_name}', the type of index must be int type or slice type, " 236 f"but got {type(index).__name__}") 237 prefix, key_index = _get_prefix_and_index(self._cells) 238 temp_dict = OrderedDict() 239 for idx, key in enumerate(self._cells.keys()): 240 cell = self._cells[key] 241 if self._is_dynamic_name[idx]: 242 for _, param in cell.parameters_and_names(): 243 param.name = prefix + str(idx) + "." + ".".join(param.name.split(".")[key_index+1:]) 244 temp_dict[str(idx)] = cell 245 else: 246 temp_dict[key] = cell 247 self._cells = temp_dict 248 self.cell_list = list(self._cells.values()) 249 250 def __bool__(self): 251 return len(self._cells) != 0 252 253 def __len__(self): 254 return len(self._cells) 255 256 def set_grad(self, flag=True): 257 self.requires_grad = flag 258 for cell in self._cells.values(): 259 cell.set_grad(flag) 260 261 def append(self, cell): 262 """ 263 Appends a given Cell to the end of the list. 264 265 Args: 266 cell(Cell): The Cell to be appended. 267 268 Examples: 269 >>> import mindspore 270 >>> from mindspore import Tensor, nn 271 >>> import numpy as np 272 >>> 273 >>> conv = nn.Conv2d(3, 2, 3, pad_mode='valid', weight_init="ones") 274 >>> bn = nn.BatchNorm2d(2) 275 >>> relu = nn.ReLU() 276 >>> seq = nn.SequentialCell([conv, bn]) 277 >>> seq.append(relu) 278 >>> x = Tensor(np.ones([1, 3, 4, 4]), dtype=mindspore.float32) 279 >>> output = seq(x) 280 >>> print(output) 281 [[[[26.999863 26.999863] 282 [26.999863 26.999863]] 283 [[26.999863 26.999863] 284 [26.999863 26.999863]]]] 285 """ 286 if _valid_cell(cell, self.__class__.__name__): 287 prefix, _ = _get_prefix_and_index(self._cells) 288 cell.update_parameters_name(prefix + str(len(self)) + ".") 289 self._is_dynamic_name.append(True) 290 self._cells[str(len(self))] = cell 291 self.cell_list = list(self._cells.values()) 292 293 def construct(self, input_data): 294 for cell in self.cell_list: 295 input_data = cell(input_data) 296 return input_data 297 298 def _insert(self, index, cell): 299 """ 300 Inserts a given Cell before a given index in the list. 301 302 Args: 303 index(int): The Insert index in the CellList. 304 cell(Cell): The Cell to be inserted. 305 """ 306 cls_name = self.__class__.__name__ 307 idx = _valid_index_for_inserting(len(self), index, cls_name) 308 _valid_cell(cell, cls_name) 309 length = len(self) 310 prefix, key_index = _get_prefix_and_index(self._cells) 311 while length > idx: 312 if self._auto_prefix: 313 tmp_cell = self._cells[str(length-1)] 314 for _, param in tmp_cell.parameters_and_names(): 315 param.name = f'{prefix}{str(length)}{"."}{".".join(param.name.split(".")[key_index+1:])}' 316 self._cells[str(length)] = self._cells[str(length - 1)] 317 length -= 1 318 self._cells[str(idx)] = cell 319 if self._auto_prefix: 320 cell.update_parameters_name(prefix + str(idx) + ".") 321 self.cell_list = list(self._cells.values()) 322 self._is_dynamic_name.insert(index, True) 323 324 325class CellList(_CellListBase, Cell): 326 """ 327 Holds Cells in a list. For more details about Cell, please refer to 328 `Cell <https://www.mindspore.cn/docs/en/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell>`_. 329 330 CellList can be used like a regular Python list, the Cells it contains have been initialized and 331 the types of Cells it contains can not be CellDict. 332 Unlike the SequentialCell, the cells in CellList are not connected. 333 334 Args: 335 args (list, optional): List of subclass of Cell. 336 337 Supported Platforms: 338 ``Ascend`` ``GPU`` ``CPU`` 339 340 Examples: 341 >>> import mindspore as ms 342 >>> import numpy as np 343 >>> 344 >>> conv = ms.nn.Conv2d(100, 20, 3) 345 >>> bn = ms.nn.BatchNorm2d(20) 346 >>> relu = ms.nn.ReLU() 347 >>> cell_ls = ms.nn.CellList([bn]) 348 >>> cell_ls.insert(0, conv) 349 >>> cell_ls.append(relu) 350 >>> cell_ls.extend([relu, relu]) 351 >>> cell_ls_3 = cell_ls[3] 352 >>> input1 = ms.Tensor(np.ones([2, 3]), ms.float32) 353 >>> output = cell_ls_3(input1) 354 >>> print(output) 355 [[1. 1. 1.] 356 [1. 1. 1.]] 357 """ 358 def __init__(self, *args, **kwargs): 359 """Initialize CellList.""" 360 auto_prefix = kwargs["auto_prefix"] if "auto_prefix" in kwargs.keys() else True 361 _CellListBase.__init__(self) 362 Cell.__init__(self, auto_prefix) 363 if len(args) == 1: 364 self.extend(args[0]) 365 366 def __getitem__(self, index): 367 cls_name = self.__class__.__name__ 368 if isinstance(index, slice): 369 return CellList(list(self._cells.values())[index]) 370 if isinstance(index, int): 371 index = _valid_index(len(self), index, cls_name) 372 return self._cells[str(index)] 373 raise TypeError(f"For '{cls_name}', the type of 'index' must be int or slice, " 374 f"but got {type(index).__name__}.") 375 376 def __setitem__(self, index, cell): 377 cls_name = self.__class__.__name__ 378 if not isinstance(index, int) and _valid_cell(cell, cls_name): 379 raise TypeError(f"For '{cls_name}', the type of 'index' must be int, " 380 f"but got {type(index).__name__}.") 381 index = _valid_index(len(self), index, cls_name) 382 if self._auto_prefix: 383 prefix, _ = _get_prefix_and_index(self._cells) 384 cell.update_parameters_name(prefix + str(index) + ".") 385 self._cells[str(index)] = cell 386 387 def __delitem__(self, index): 388 cls_name = self.__class__.__name__ 389 if isinstance(index, int): 390 index = _valid_index(len(self), index, cls_name) 391 del self._cells[str(index)] 392 elif isinstance(index, slice): 393 keys = list(self._cells.keys())[index] 394 for key in keys: 395 del self._cells[key] 396 else: 397 raise TypeError(f"For '{cls_name}', the type of 'index' must be int or slice, " 398 f"but got {type(index).__name__}.") 399 # adjust orderedDict 400 prefix, key_index = _get_prefix_and_index(self._cells) 401 temp_dict = OrderedDict() 402 for idx, cell in enumerate(self._cells.values()): 403 if self._auto_prefix: 404 for _, param in cell.parameters_and_names(): 405 param.name = prefix + str(idx) + "." + ".".join(param.name.split(".")[key_index+1:]) 406 temp_dict[str(idx)] = cell 407 self._cells = temp_dict 408 409 def __bool__(self): 410 return len(self._cells) != 0 411 412 def __len__(self): 413 return len(self._cells) 414 415 def __iter__(self): 416 return iter(self._cells.values()) 417 418 def __iadd__(self, cells): 419 self.extend(cells) 420 return self 421 422 def insert(self, index, cell): 423 """ 424 Inserts a given Cell before a given index in the list. 425 426 Args: 427 index(int): The Insert index in the CellList. 428 cell(Cell): The Cell to be inserted. 429 """ 430 cls_name = self.__class__.__name__ 431 idx = _valid_index_for_inserting(len(self), index, cls_name) 432 _valid_cell(cell, cls_name) 433 length = len(self) 434 prefix, key_index = _get_prefix_and_index(self._cells) 435 while length > idx: 436 if self._auto_prefix: 437 tmp_cell = self._cells[str(length-1)] 438 for _, param in tmp_cell.parameters_and_names(): 439 param.name = prefix + str(length) + "." + ".".join(param.name.split(".")[key_index+1:]) 440 self._cells[str(length)] = self._cells[str(length - 1)] 441 length -= 1 442 self._cells[str(idx)] = cell 443 if self._auto_prefix: 444 cell.update_parameters_name(prefix + str(idx) + ".") 445 446 def extend(self, cells): 447 """ 448 Appends Cells from a Python iterable to the end of the list. 449 450 Args: 451 cells(list): The Cells to be extended, the types of Cells can not be CellDict. 452 453 Raises: 454 TypeError: If the argument cells are not a list of Cells. 455 """ 456 cls_name = self.__class__.__name__ 457 if not isinstance(cells, list): 458 raise TypeError(f"For '{cls_name}', the new cells wanted to append " 459 f"should be instance of list, but got {type(cells).__name__}.") 460 prefix, _ = _get_prefix_and_index(self._cells) 461 for cell in cells: 462 if isinstance(cell, CellDict): 463 raise TypeError(f"For '{cls_name}', the type of cell can not be CellDict, " 464 f"but got {type(cell).__name__}.") 465 if _valid_cell(cell, cls_name): 466 if self._auto_prefix: 467 cell.update_parameters_name(prefix + str(len(self)) + ".") 468 self._cells[str(len(self))] = cell 469 return self 470 471 def append(self, cell): 472 """ 473 Appends a given Cell to the end of the list. 474 475 Args: 476 cell(Cell): The subcell to be appended. 477 """ 478 if _valid_cell(cell, self.__class__.__name__): 479 if self._auto_prefix: 480 prefix, _ = _get_prefix_and_index(self._cells) 481 cell.update_parameters_name(prefix + str(len(self)) + ".") 482 self._cells[str(len(self))] = cell 483 484 def set_grad(self, flag=True): 485 self.requires_grad = flag 486 for cell in self._cells.values(): 487 cell.set_grad(flag) 488 489 def construct(self, *inputs): 490 raise NotImplementedError 491 492 493class _CellDictBase: 494 """ 495 An interface for base the Cell as dict. 496 497 The sequential Cell may be iterated using the construct method using for-in statement. 498 But there are some scenarios that the construct method built-in does not fit. 499 For convenience, we provide an interface that indicates the sequential 500 Cell may be interpreted as dict of Cells, so it can be accessed using 501 key when a sequential Cell instantiate is accessed by key, 502 it will be interpreted as a dict of Cells. 503 """ 504 def __init__(self): 505 """Initialize _CellDictBase.""" 506 self.__cell_as_dict__ = True 507 508 @abstractmethod 509 def __len__(self): 510 pass 511 512 @abstractmethod 513 def __getitem__(self, index): 514 pass 515 516 def construct(self): 517 raise NotImplementedError 518 519 520class CellDict(_CellDictBase, Cell): 521 """ 522 Holds Cells in a dictionary. For more details about `Cell` , please refer to :class:`mindspore.nn.Cell` . 523 524 `CellDict` can be used like a regular Python dictionary. 525 526 Args: 527 args (iterable, optional): An iterable of key-value pairs of (key, Cell), the type of key-value pairs is 528 (string, Cell); Or a mapping(dictionary) from string to Cell. 529 The type of Cell can not be CellDict, CellList or SequentialCell. 530 The key can not be same with the attributes of class Cell, can not contain '.', 531 can not be an empty string. 532 The key of type string is used to search corresponding Cell in the CellDict. 533 kwargs (dict): Reserved for keyword argument to be expanded. 534 535 Supported Platforms: 536 ``Ascend`` ``GPU`` ``CPU`` 537 538 Examples: 539 >>> import collections 540 >>> from collections import OrderedDict 541 >>> import mindspore as ms 542 >>> import numpy as np 543 >>> from mindspore import Tensor, nn 544 >>> 545 >>> cell_dict = nn.CellDict({'conv': nn.Conv2d(10, 6, 5), 546 ... 'relu': nn.ReLU(), 547 ... 'max_pool2d': nn.MaxPool2d(kernel_size=4, stride=4)}) 548 >>> print(len(cell_dict)) 549 3 550 >>> cell_dict.clear() 551 >>> print(len(cell_dict)) 552 0 553 >>> ordered_cells = OrderedDict([('conv', nn.Conv2d(10, 6, 5, pad_mode='valid')), 554 ... ('relu', nn.ReLU()), 555 ... ('max_pool2d', nn.MaxPool2d(kernel_size=2, stride=2))]) 556 >>> cell_dict.update(ordered_cells) 557 >>> x = Tensor(np.ones([1, 10, 6, 10]), ms.float32) 558 >>> for cell in cell_dict.values(): 559 ... x = cell(x) 560 >>> print(x.shape) 561 (1, 6, 1, 3) 562 >>> x = Tensor(np.ones([1, 10, 6, 10]), ms.float32) 563 >>> for item in cell_dict.items(): 564 ... x = item[1](x) 565 >>> print(x.shape) 566 (1, 6, 1, 3) 567 >>> print(cell_dict.keys()) 568 odict_keys(['conv', 'relu', 'max_pool2d']) 569 >>> pop_cell = cell_dict.pop('conv') 570 >>> x = Tensor(np.ones([1, 10, 6, 5]), ms.float32) 571 >>> x = pop_cell(x) 572 >>> print(x.shape) 573 (1, 6, 2, 1) 574 >>> print(len(cell_dict)) 575 2 576 """ 577 def __init__(self, *args, **kwargs): 578 """Initialize CellDict.""" 579 auto_prefix = kwargs["auto_preifx"] if "auto_prefix" in kwargs.keys() else True 580 _CellDictBase.__init__(self) 581 Cell.__init__(self, auto_prefix) 582 if len(args) == 1: 583 self.update(args[0]) 584 585 def __getitem__(self, key): 586 return self._cells[key] 587 588 def __setitem__(self, key, cell): 589 self._validate_key(key) 590 self._validate_cell_type(cell) 591 self._update_cell_para_name(key, cell) 592 self._cells[key] = cell 593 594 def __delitem__(self, key): 595 del self._cells[key] 596 597 def __len__(self): 598 return len(self._cells) 599 600 def __iter__(self): 601 return iter(self._cells) 602 603 def __contains__(self, key): 604 return key in self._cells 605 606 def _validate_key(self, key): 607 """validate key.""" 608 cls_name = self.__class__.__name__ 609 if not isinstance(key, str): 610 raise TypeError(f"For '{cls_name}', the type of key should be string " 611 f"but got {type(key).__name__}.") 612 if hasattr(self, key) and key not in self._cells: 613 raise KeyError(f"For '{cls_name}', the key can not be same with the attributes of Cell, " 614 f"but got key {key}.") 615 if '.' in key: 616 raise KeyError(f"For '{cls_name}', key can not contain \".\", " 617 f"but got key {key}") 618 if key == '': 619 raise KeyError(f"For '{cls_name}', key can not be empty string \"\", " 620 f"but got key {key}") 621 622 def _validate_cell_type(self, cell): 623 """validate cell type.""" 624 cls_name = self.__class__.__name__ 625 if cell is None: 626 raise TypeError(f"For '{cls_name}', cell can not be None.") 627 if not isinstance(cell, Cell): 628 raise TypeError(f"For '{cls_name}', the type of cell should be Cell, " 629 f"but got {type(cell).__name__}.") 630 if isinstance(cell, (CellDict, CellList, SequentialCell)): 631 raise TypeError(f"For '{cls_name}', the type of cell can not be CellDict, CellList or SequentialCell, " 632 f"but got {type(cell).__name__}.") 633 634 def _update_cell_para_name(self, key, cell): 635 """update cell para name.""" 636 if self._auto_prefix: 637 prefix, _ = _get_prefix_and_index(self._cells) 638 cell.update_parameters_name(prefix + key + ".") 639 640 def clear(self): 641 """ 642 Remove all Cells from the CellDict. 643 """ 644 return self._cells.clear() 645 646 def pop(self, key): 647 """ 648 Remove key from the CellDict and return its cell. 649 650 Args: 651 key (string): key to pop from the CellDict. 652 653 Raises: 654 KeyError: If `key` not exist in CellDict when attempt to access cell. 655 """ 656 value = self[key] 657 del self[key] 658 return value 659 660 def keys(self): 661 """ 662 Return an iterable of the CellDict keys. 663 664 Returns: 665 An iterable object. 666 """ 667 return self._cells.keys() 668 669 def values(self): 670 """ 671 Return an iterable of the CellDict values. 672 673 Returns: 674 An iterable object. 675 """ 676 return self._cells.values() 677 678 def items(self): 679 """ 680 Return an iterable of the CellDict key-value pairs. 681 682 Returns: 683 An iterable object. 684 """ 685 return self._cells.items() 686 687 def update(self, cells): 688 """ 689 Update the CellDict by overwriting the existing keys with the key-value pairs from a mapping or an iterable. 690 691 Args: 692 cells (iterable): An iterable of key-value pairs of (key, Cell), the type of key-value pairs is 693 (string, Cell); Or a mapping(dictionary) from string to Cell. 694 The type of Cell can not be CellDict, CellList or SequentialCell. 695 The key can not be same with the attributes of class Cell, can not contain '.', 696 can not be an empty string. 697 698 Note: 699 If the `cells` is a CellDict, an OrderedDict or an iterable containing key-value pairs, 700 the order of newly added elements is maintained. 701 702 Raises: 703 TypeError: If `cells` is not an iterable object. 704 TypeError: If key-value pairs in `cells` are not iterable objects. 705 ValueError: If the length of key-value pairs in `cells` is not 2. 706 TypeError: If the cell in `cells` is None. 707 TypeError: If the type of cell in `cells` is not Cell. 708 TypeError: If the type of cell in `cells` is CellDict, CellList or SequentialCell. 709 TypeError: If the type of key in `cells` is not string. 710 KeyError: If the key in `cells` is same with the attributes of class Cell. 711 KeyError: If the key in `cells` contain ".". 712 KeyError: If the key in `cells` is an empty string. 713 """ 714 if not isinstance(cells, abc.Iterable): 715 raise TypeError("CellDict.update() should be called with an " 716 "iterable of key-value pairs, but got " + 717 type(cells).__name__) 718 if isinstance(cells, (OrderedDict, CellDict, abc.Mapping)): 719 for key, cell in cells.items(): 720 self[key] = cell 721 else: 722 for id, k_v in enumerate(cells): 723 if not isinstance(k_v, abc.Iterable): 724 raise TypeError("CellDict update sequence element " 725 "#" + str(id) + " should be Iterable; but got " + 726 type(k_v).__name__) 727 if len(k_v) != 2: 728 raise ValueError("CellDict update sequence element " 729 "#" + str(id) + ", length should be 2; but has length " + 730 str(len(k_v))) 731 self[k_v[0]] = k_v[1] 732 733 def construct(self, *inputs): 734 raise NotImplementedError 735