def _hash(self, obj, parent, parents_ids=EMPTY_FROZENSET):
        """The main diff method"""

        try:
            result = self[obj]
        except (TypeError, KeyError):
            pass
        else:
            return result

        result = not_hashed

        if self._skip_this(obj, parent):
            return

        elif obj is None:
            result = 'NONE'

        elif isinstance(obj, strings):
            result = prepare_string_for_hashing(
                obj, ignore_string_type_changes=self.ignore_string_type_changes,
                ignore_string_case=self.ignore_string_case)

        elif isinstance(obj, numbers):
            result = self._prep_number(obj)

        elif isinstance(obj, MutableMapping):
            result = self._prep_dict(obj=obj, parent=parent, parents_ids=parents_ids)

        elif isinstance(obj, tuple):
            result = self._prep_tuple(obj=obj, parent=parent, parents_ids=parents_ids)

        elif isinstance(obj, Iterable):
            result = self._prep_iterable(obj=obj, parent=parent, parents_ids=parents_ids)

        else:
            result = self._prep_obj(obj=obj, parent=parent, parents_ids=parents_ids)

        if result is not_hashed:  # pragma: no cover
            self[UNPROCESSED].append(obj)

        elif result is unprocessed:
            pass

        elif self.apply_hash:
            if isinstance(obj, strings):
                result_cleaned = result
            else:
                result_cleaned = prepare_string_for_hashing(
                    result, ignore_string_type_changes=self.ignore_string_type_changes,
                    ignore_string_case=self.ignore_string_case)
            result = self.hasher(result_cleaned)

        # It is important to keep the hash of all objects.
        # The hashes will be later used for comparing the objects.
        try:
            self[obj] = result
        except TypeError:
            obj_id = get_id(obj)
            self[obj_id] = result

        return result