class_docstr: Special case the hasher for when numpy is loaded.
      
class_docstr_tok: ["Special","case","the","hasher","for","when","numpy","is","loaded","."]
docstr: Subclass the save method, to hash ndarray subclass, rather
  than pickling them. Off course, this is a total abuse of
  the Pickler class.
docstr_tok: ["Subclass","the","save","method",",","to","hash","ndarray","subclass",",","rather","than","pickling","them",".","Off","course",",","this","is","a","total","abuse","of","the","Pickler","class","."]
code: def save(self, obj):
      """Subclass the save method, to hash ndarray subclass, rather
      than pickling them. Off course, this is a total abuse of
      the Pickler class.
      """
      if isinstance(obj, self.np.ndarray) and not obj.dtype.hasobject:
          # Compute a hash of the object
          # The update function of the hash requires a c_contiguous buffer.
          if obj.shape == ():
              # 0d arrays need to be flattened because viewing them as bytes
              # raises a ValueError exception.
              obj_c_contiguous = obj.flatten()
          elif obj.flags.c_contiguous:
              obj_c_contiguous = obj
          elif obj.flags.f_contiguous:
              obj_c_contiguous = obj.T
          else:
              # Cater for non-single-segment arrays: this creates a
              # copy, and thus alleviates this issue.
              # XXX: There might be a more efficient way of doing this
              obj_c_contiguous = obj.flatten()
  
          # memoryview is not supported for some dtypes, e.g. datetime64, see
          # https://github.com/numpy/numpy/issues/4983. The
          # workaround is to view the array as bytes before
          # taking the memoryview.
          self._hash.update(
              self._getbuffer(obj_c_contiguous.view(self.np.uint8))
          )
  
          # We store the class, to be able to distinguish between
          # Objects with the same binary content, but different
          # classes.
          if self.coerce_mmap and isinstance(obj, self.np.memmap):
              # We don't make the difference between memmap and
              # normal ndarrays, to be able to reload previously
              # computed results with memmap.
              klass = self.np.ndarray
          else:
              klass = obj.__class__
          # We also return the dtype and the shape, to distinguish
          # different views on the same data with different dtypes.
  
          # The object will be pickled by the pickler hashed at the end.
          obj = (klass, ("HASHED", obj.dtype, obj.shape, obj.strides))
      elif isinstance(obj, self.np.dtype):
          # numpy.dtype consistent hashing is tricky to get right. This comes
          # from the fact that atomic np.dtype objects are interned:
          # ``np.dtype('f4') is np.dtype('f4')``. The situation is
          # complicated by the fact that this interning does not resist a
          # simple pickle.load/dump roundtrip:
          # ``pickle.loads(pickle.dumps(np.dtype('f4'))) is not
          # np.dtype('f4') Because pickle relies on memoization during
          # pickling, it is easy to
          # produce different hashes for seemingly identical objects, such as
          # ``[np.dtype('f4'), np.dtype('f4')]``
          # and ``[np.dtype('f4'), pickle.loads(pickle.dumps('f4'))]``.
          # To prevent memoization from interfering with hashing, we isolate
          # the serialization (and thus the pickle memoization) of each dtype
          # using each time a different ``pickle.dumps`` call unrelated to
          # the current Hasher instance.
          self._hash.update("_HASHED_DTYPE".encode("utf-8"))
          self._hash.update(pickle.dumps(obj))
          return
      Hasher.save(self, obj)
  
imports: import pickle
  import hashlib
  import sys
  import types
  import struct
  import io
  import decimal
label_desc: Use image node_id 2 for calling the NumpyHasher obj's underlying member method code with example usage: obj.save(obj) without return types