text: # -*- coding=utf-8 -*-
  # author: king
  # datetime: 2019/10/30 10:50
  # describtion: 
  """
      json模块源码分析
      以下代码是从json模块里剥离出来的核心encode代码
      可以直接测试案列
  """
  import re
  
  
  INFINITY = float('inf')
  
  ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
  ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
  HAS_UTF8 = re.compile(b'[\x80-\xff]')
  ESCAPE_DCT = {
      '\\': '\\\\',
      '"': '\\"',
      '\b': '\\b',
      '\f': '\\f',
      '\n': '\\n',
      '\r': '\\r',
      '\t': '\\t',
  }
  for i in range(0x20):
      ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
  
  
  def py_encode_basestring_ascii(s):
      """Return an ASCII-only JSON representation of a Python string
  
      """
      def replace(match):
          s = match.group(0)
          try:
              return ESCAPE_DCT[s]
          except KeyError:
              n = ord(s)
              if n < 0x10000:
                  return '\\u{0:04x}'.format(n)
              else:
                  # surrogate pair
                  n -= 0x10000
                  s1 = 0xd800 | ((n >> 10) & 0x3ff)
                  s2 = 0xdc00 | (n & 0x3ff)
                  return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
      return "'" + ESCAPE_ASCII.sub(replace, s) + "'"
  
  
  def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
          _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
          ## HACK: hand-optimized bytecode; turn globals into locals
          ValueError=ValueError,
          dict=dict,
          float=float,
          id=id,
          int=int,
          isinstance=isinstance,
          list=list,
          str=str,
          tuple=tuple,
          _intstr=int.__str__,
      ):
      if _indent is not None and not isinstance(_indent, str):
          _indent = ' ' * _indent
  
      def _iterencode_list(lst, _current_indent_level):  # list结构的迭代
          if not lst:
              yield '[]'
              return
          if markers is not None:
              markerid = id(lst)
              if markerid in markers:
                  raise ValueError("Circular reference detected")
              markers[markerid] = lst
          buf = '['
          if _indent is not None:
              _current_indent_level += 1
              newline_indent = '\n' + _indent * _current_indent_level
              separator = _item_separator + newline_indent
              buf += newline_indent
          else:
              newline_indent = None
              separator = _item_separator
          first = True
          for value in lst:
              if first:
                  first = False
              else:
                  buf = separator
              if isinstance(value, str):
                  yield buf + _encoder(value)
              elif value is None:
                  yield buf + 'null'
              elif value is True:
                  yield buf + 'true'
              elif value is False:
                  yield buf + 'false'
              elif isinstance(value, int):
                  # Subclasses of int/float may override __str__, but we still
                  # want to encode them as integers/floats in JSON. One example
                  # within the standard library is IntEnum.
                  yield buf + _intstr(value)
              elif isinstance(value, float):
                  # see comment above for int
                  yield buf + _floatstr(value)
              else:
                  yield buf
                  if isinstance(value, (list, tuple)):
                      chunks = _iterencode_list(value, _current_indent_level)
                  elif isinstance(value, dict):
                      chunks = _iterencode_dict(value, _current_indent_level)
                  else:
                      chunks = _iterencode(value, _current_indent_level)
                  yield from chunks
          if newline_indent is not None:
              _current_indent_level -= 1
              yield '\n' + _indent * _current_indent_level
          yield ']'
          if markers is not None:
              del markers[markerid]
  
      def _iterencode_dict(dct, _current_indent_level):  # dict结构的迭代
          if not dct:
              yield '{}'
              return
          if markers is not None:
              markerid = id(dct)
              if markerid in markers:
                  raise ValueError("Circular reference detected")
              markers[markerid] = dct
          yield '{'
          if _indent is not None:
              _current_indent_level += 1
              newline_indent = '\n' + _indent * _current_indent_level
              item_separator = _item_separator + newline_indent
              yield newline_indent
          else:
              newline_indent = None
              item_separator = _item_separator
          first = True
          if _sort_keys:
              items = sorted(dct.items(), key=lambda kv: kv[0])
          else:
              items = dct.items()
          for key, value in items:
              if isinstance(key, str):
                  pass
              # JavaScript is weakly typed for these, so it makes sense to
              # also allow them.  Many encoders seem to do something like this.
              elif isinstance(key, float):
                  # see comment for int/float in _make_iterencode
                  key = _floatstr(key)
              elif key is True:
                  key = 'true'
              elif key is False:
                  key = 'false'
              elif key is None:
                  key = 'null'
              elif isinstance(key, int):
                  # see comment for int/float in _make_iterencode
                  key = _intstr(key)
              elif _skipkeys:
                  continue
              else:
                  raise TypeError("key " + repr(key) + " is not a string")
              if first:
                  first = False
              else:
                  yield item_separator
              yield _encoder(key)
              yield _key_separator
              if isinstance(value, str):
                  yield _encoder(value)
              elif value is None:
                  yield 'null'
              elif value is True:
                  yield 'true'
              elif value is False:
                  yield 'false'
              elif isinstance(value, int):
                  # see comment for int/float in _make_iterencode
                  yield _intstr(value)
              elif isinstance(value, float):
                  # see comment for int/float in _make_iterencode
                  yield _floatstr(value)
              else:
                  if isinstance(value, (list, tuple)):
                      chunks = _iterencode_list(value, _current_indent_level)
                  elif isinstance(value, dict):
                      chunks = _iterencode_dict(value, _current_indent_level)
                  else:
                      chunks = _iterencode(value, _current_indent_level)
                  yield from chunks
          if newline_indent is not None:
              _current_indent_level -= 1
              yield '\n' + _indent * _current_indent_level
          yield '}'
          if markers is not None:
              del markers[markerid]
  
      def _iterencode(o, _current_indent_level):  # 综合迭达入口（核心）
  
          if isinstance(o, str):
              yield _encoder(o)
          elif o is None:
              yield 'null'
          elif o is True:
              yield 'true'
          elif o is False:
              yield 'false'
          elif isinstance(o, int):
              # see comment for int/float in _make_iterencode
              yield _intstr(o)
          elif isinstance(o, float):
              # see comment for int/float in _make_iterencode
              yield _floatstr(o)
          elif isinstance(o, (list, tuple)):
              yield from _iterencode_list(o, _current_indent_level)
          elif isinstance(o, dict):
              yield from _iterencode_dict(o, _current_indent_level)
          else:
              if markers is not None:
                  markerid = id(o)
                  if markerid in markers:
                      raise ValueError("Circular reference detected")
                  markers[markerid] = o
              o = _default(o)
              yield from _iterencode(o, _current_indent_level)
              if markers is not None:
                  del markers[markerid]
      return _iterencode
  
  
  class JSONEncoder(object):
      """Extensible JSON <http://json.org> encoder for Python data structures.
      """
      item_separator = ', '
      key_separator = ': '
      def __init__(self, *, skipkeys=False, ensure_ascii=True,
              check_circular=True, allow_nan=True, sort_keys=False,
              indent=None, separators=None, default=None):
          """Constructor for JSONEncoder, with sensible defaults.
          """
  
          self.skipkeys = skipkeys              # 非法的key值是否跳过
          self.ensure_ascii = ensure_ascii      # 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
          self.check_circular = check_circular  # 检查循环引用，防止内存溢出
          self.allow_nan = allow_nan            # 跳过对nan，Infinity，and-Infinity的编码（不符json规范但是符合JS解释器规范）
          self.sort_keys = sort_keys            # 输出对key进行排序
          self.indent = indent                  # 格式化输出缩进空格数
          if separators is not None:  # 指定分隔符
              self.item_separator, self.key_separator = separators
          elif indent is not None:
              self.item_separator = ','
          if default is not None:
              self.default = default
  
      def default(self, o):
          """
          """
          raise TypeError("Object of type '%s' is not JSON serializable" %
                          o.__class__.__name__)
  
      def encode(self, o):
          """Return a JSON string representation of a Python data structure.
  
          >>> from json.encoder import JSONEncoder
          >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
          '{"foo": ["bar", "baz"]}'
  
          """
          # This is for extremely simple cases and benchmarks.
          # This doesn't pass the iterator directly to ''.join() because the
          # exceptions aren't as detailed.  The list call should be roughly
          # equivalent to the PySequence_Fast that ''.join() would do.
          chunks = self.iterencode(o, _one_shot=True)  # return ["[{'foo' : ['bar', 'baz'], 'null':'sdfd'}]"]
          return ''.join(chunks)
  
      def iterencode(self, o, _one_shot=False):
          """Encode the given object and yield each string
          representation as available.
          For example::
  
              for chunk in JSONEncoder().iterencode(bigobject):
                  mysocket.write(chunk)
  
          """
          if self.check_circular:
              markers = {}
          else:
              markers = None
  
          def floatstr(o, allow_nan=self.allow_nan,
                  _repr=float.__repr__, _inf=INFINITY, _neginf=-INFINITY):
              # Check for specials.  Note that this type of test is processor
              # and/or platform-specific, so do tests which don't depend on the
              # internals.
  
              if o != o:
                  text = 'NaN'
              elif o == _inf:
                  text = 'Infinity'
              elif o == _neginf:
                  text = '-Infinity'
              else:
                  return _repr(o)
  
              if not allow_nan:
                  raise ValueError(
                      "Out of range float values are not JSON compliant: " +
                      repr(o))
  
              return text
  
          # 这里直接使用了py功能实现，另外一个是c实现的具体功能
          _encoder = (py_encode_basestring_ascii)  # encode_basestring_ascii or
  
          _iterencode = _make_iterencode(
              markers, self.default, _encoder, self.indent, floatstr,
              self.key_separator, self.item_separator, self.sort_keys,
              self.skipkeys, _one_shot)
  
          return _iterencode(o, 0)
  
  
  if __name__ == '__main__':
      # from json.encoder import JSONEncoder  # 原始
      res = JSONEncoder().encode([{'foo': ["bar", "baz"], None:"sdfd"}])
      print("res >>", res)
  