code: def cache_results(
      _cache_fp: str,
      _hash_param: bool = True,
      _refresh: bool = False,
      _verbose: int = 1,
      _check_hash: bool = True,
  ):
      r"""
      :func:`cache_results` 是 **fastNLP** 中用于缓存数据的装饰器。通过下面的例子看一下如何使用::
  
          import time
          import numpy as np
          from fastNLP import cache_results
  
          @cache_results('cache.pkl')
          def process_data(second=1):
              # 一些比较耗时的工作，比如读取数据，预处理数据等，这里用time.sleep()代替耗时
              time.sleep(second)
              return np.random.randint(10, size=(5,))
  
          start_time = time.time()
          print("res =",process_data())
          print(time.time() - start_time)
  
          start_time = time.time()
          print("res =",process_data())
          print(time.time() - start_time)
  
          start_time = time.time()
          print("res =",process_data(second=2))
          print(time.time() - start_time)
  
          # 输出内容如下，可以看到前两次结果相同，且第二次几乎没有花费时间。第三次由于参数变化了，所以cache的结果也就自然变化了。
          # Save cache to 2d145aeb_cache.pkl.
          # res = [5 4 9 1 8]
          # 1.0134737491607666
          # Read cache from 2d145aeb_cache.pkl (Saved on xxxx).
          # res = [5 4 9 1 8]
          # 0.0040721893310546875
          # Save cache to 0ead3093_cache.pkl.
          # res = [1 8 2 5 1]
          # 2.0086121559143066
  
      可以看到第二次运行的时候，只用了 0.0001s 左右，这是由于第二次运行将直接从cache.pkl这个文件读取数据，而不会经过再次预处理。
      如果在函数加上了装饰器 ``@cache_results()``，则函数会增加五个参数 ``[_cache_fp, _hash_param, _refresh, _verbose,
      _check_hash]``。上面的例子即为使用_cache_fp的情况，这五个参数不会传入到被装饰函数中，当然被装饰函数参数名也不能包含这五个名称。
  
      :param _cache_fp: 将返回结果缓存到什么位置;或从什么位置读取缓存。如果为 ``None`` ，cache_results 没有任何效用，除非在
          函数调用的时候传入 _cache_fp 这个参数。实际保存的文件名会受到 ``_hash_param`` 参数的影响，例如传入的名称是 **"caches/cache.pkl"**，
          实际保存的文件名会是 **"caches/{hash_param_result}_cache.pkl"**。
      :param _hash_param: 是否将传入给被装饰函数的 parameter 进行 :func:`str` 之后的 hash 结果加入到 ``_cache_fp`` 中，这样每次函数的
          parameter 改变的时候，cache 文件就自动改变了。
      :param _refresh: 强制重新生成新的 cache 。
      :param _verbose: 是否打印 cache 的信息。
      :param _check_hash: 如果为 ``True`` 将尝试对比修饰的函数的源码以及该函数内部调用的函数的源码的 hash 值。如果发现保存时的 hash 值
          与当前的 hash 值有差异，会报 warning 。但该 warning 可能出现实质上并不影响结果的误报（例如增删空白行）；且在修改不涉及源码时，虽然
          该修改对结果有影响，但无法做出 warning。
      :return:
      """
  
      def wrapper_(func):
          signature = inspect.signature(func)
          for key, _ in signature.parameters.items():
              if key in (
                  "_cache_fp",
                  "_hash_param",
                  "_refresh",
                  "_verbose",
                  "_check_hash",
              ):
                  raise RuntimeError(
                      "The function decorated by cache_results cannot have keyword `{}`.".format(
                          key
                      )
                  )
  
          @functools.wraps(func)
          def wrapper(*args, **kwargs):
              # fn_param = kwargs.copy()
              # if args:
              #     params = [p.name for p in inspect.signature(func).parameters.values()]
              #     fn_param.update(zip(params, args))
              if "_cache_fp" in kwargs:
                  cache_filepath = kwargs.pop("_cache_fp")
                  assert isinstance(
                      cache_filepath, str
                  ), "_cache_fp can only be str."
              else:
                  cache_filepath = _cache_fp
              if "_refresh" in kwargs:
                  refresh = kwargs.pop("_refresh")
                  assert isinstance(
                      refresh, bool
                  ), "_refresh can only be bool."
              else:
                  refresh = _refresh
              if "_verbose" in kwargs:
                  verbose = kwargs.pop("_verbose")
                  assert isinstance(
                      verbose, int
                  ), "_verbose can only be integer."
              else:
                  verbose = _verbose
  
              if "_check_hash" in kwargs:
                  check_hash = kwargs.pop("_check_hash")
              else:
                  check_hash = _check_hash
  
              if "_hash_param" in kwargs:
                  hash_param = kwargs.pop("_hash_param")
                  assert isinstance(
                      hash_param, bool
                  ), "_hash_param can only be bool."
              else:
                  hash_param = _hash_param
  
              if (
                  hash_param and cache_filepath is not None
              ):  # 尝试将parameter给hash一下
                  try:
                      params = dict(
                          inspect.getcallargs(func, *args, **kwargs)
                      )
                      if inspect.ismethod(
                          func
                      ):  # 如果是 method 的话第一个参数（一般就是 self ）就不考虑了
                          first_key = next(iter(params.items()))
                          params.pop(first_key)
                      if len(params):
                          # sort 一下防止顺序改变
                          params = {
                              k: str(v)
                              for k, v in sorted(
                                  params.items(),
                                  key=lambda item: item[0],
                              )
                          }
                          param_hash = cal_fn_hash_code(None, params)[
                              :8
                          ]
                          head, tail = os.path.split(cache_filepath)
                          cache_filepath = os.path.join(
                              head, param_hash + "_" + tail
                          )
                  except BaseException as e:
                      logger.debug(
                          f"Fail to add parameter hash to cache path, because of Exception:{e}"
                      )
  
              refresh_flag = True
              new_hash_code = None
              if check_hash:
                  new_hash_code = cal_fn_hash_code(func, None)
  
              if cache_filepath is not None and refresh is False:
                  # load data
                  if os.path.exists(cache_filepath):
                      cache_filepath = os.path.abspath(cache_filepath)
                      with open(cache_filepath, "rb") as f:
                          results = _pickle.load(f)
                          old_hash_code = results["hash"]
                          save_time = results["save_time"]
                          results = results["results"]
                      if verbose == 1:
                          logger.info(
                              "Read cache from {} (Saved on {}).".format(
                                  cache_filepath, save_time
                              )
                          )
                      if check_hash and old_hash_code != new_hash_code:
                          logger.warning(
                              f"The function {_get_fun_msg(func)} is different from its last cache (Save on {save_time}). The "
                              f"difference may caused by the sourcecode change.",
                              extra={
                                  "highlighter": ColorHighlighter("red")
                              },
                          )
                      refresh_flag = False
  
              if refresh_flag:
                  if new_hash_code is None:
                      new_hash_code = cal_fn_hash_code(func, None)
                  results = func(*args, **kwargs)
                  if cache_filepath is not None:
                      if results is None:
                          raise RuntimeError(
                              "The return value is None. Cannot save None results."
                          )
                      cache_filepath = os.path.abspath(cache_filepath)
                      _prepare_cache_filepath(cache_filepath)
                      _dict = {
                          "results": results,
                          "hash": new_hash_code,
                          "save_time": datetime.now(),
                      }
                      with open(cache_filepath, "wb") as f:
                          _pickle.dump(_dict, f)
                      logger.info(
                          "Save cache to {}.".format(cache_filepath)
                      )
  
              return results
  
          return wrapper
  
      return wrapper_
  
imports: from datetime import datetime
  import hashlib
  import _pickle
  import functools
  import os
  import re
  from typing import Callable, List, Any, Optional
  import inspect
  import ast
  from collections import deque
  from fastNLP.core.log.logger import logger
  from fastNLP.core.log.highlighter import ColorHighlighter
  from .utils import _get_fun_msg
label_desc: Use image node_id 6 for calling a global function with example usage: cache_results(_cache_fp, _hash_param, _refresh, _verbose, _check_hash) and returns: wrapper_, wrapper, results