This is the clear winner over the generate version.
    Used by hash_data

    Ignore:
        import utool
        rng = np.random.RandomState(0)
        # str1 = rng.rand(0).dumps()
        str1 = b'SEP'
        str2 = rng.rand(10000).dumps()
        for timer in utool.Timerit(100, label='twocall'):
            hasher = hashlib.sha256()
            with timer:
                hasher.update(str1)
                hasher.update(str2)
        a = hasher.hexdigest()
        for timer in utool.Timerit(100, label='concat'):
            hasher = hashlib.sha256()
            with timer:
                hasher.update(str1 + str2)
        b = hasher.hexdigest()
        assert a == b
        # CONCLUSION: Faster to concat in case of prefixes and seps

        nested_data = {'1': [rng.rand(100), '2', '3'],
                       '2': ['1', '2', '3', '4', '5'],
                       '3': [('1', '2'), ('3', '4'), ('5', '6')]}
        data = list(nested_data.values())


        for timer in utool.Timerit(1000, label='cat-generate'):
            hasher = hashlib.sha256()
            with timer:
                hasher.update(b''.join(_bytes_generator(data)))

        for timer in utool.Timerit(1000, label='inc-generate'):
            hasher = hashlib.sha256()
            with timer:
                for b in _bytes_generator(data):
                    hasher.update(b)

        for timer in utool.Timerit(1000, label='inc-generate'):
            hasher = hashlib.sha256()
            with timer:
                for b in _bytes_generator(data):
                    hasher.update(b)

        for timer in utool.Timerit(1000, label='chunk-inc-generate'):
            hasher = hashlib.sha256()
            import ubelt as ub
            with timer:
                for chunk in ub.chunks(_bytes_generator(data), 5):
                    hasher.update(b''.join(chunk))

        for timer in utool.Timerit(1000, label='inc-update'):
            hasher = hashlib.sha256()
            with timer:
                _update_hasher(hasher, data)

        data = ut.lorium_ipsum()
        hash_data(data)
        ut.hashstr27(data)
        %timeit hash_data(data)
        %timeit ut.hashstr27(repr(data))

        for timer in utool.Timerit(100, label='twocall'):
            hasher = hashlib.sha256()
            with timer:
                hash_data(data)

        hasher = hashlib.sha256()
        hasher.update(memoryview(np.array([1])))
        print(hasher.hexdigest())

        hasher = hashlib.sha256()
        hasher.update(np.array(['1'], dtype=object))
        print(hasher.hexdigest())

def _update_hasher(hasher, data):
    """
    This is the clear winner over the generate version.
    Used by hash_data

    Ignore:
        import utool
        rng = np.random.RandomState(0)
        # str1 = rng.rand(0).dumps()
        str1 = b'SEP'
        str2 = rng.rand(10000).dumps()
        for timer in utool.Timerit(100, label='twocall'):
            hasher = hashlib.sha256()
            with timer:
                hasher.update(str1)
                hasher.update(str2)
        a = hasher.hexdigest()
        for timer in utool.Timerit(100, label='concat'):
            hasher = hashlib.sha256()
            with timer:
                hasher.update(str1 + str2)
        b = hasher.hexdigest()
        assert a == b
        # CONCLUSION: Faster to concat in case of prefixes and seps

        nested_data = {'1': [rng.rand(100), '2', '3'],
                       '2': ['1', '2', '3', '4', '5'],
                       '3': [('1', '2'), ('3', '4'), ('5', '6')]}
        data = list(nested_data.values())


        for timer in utool.Timerit(1000, label='cat-generate'):
            hasher = hashlib.sha256()
            with timer:
                hasher.update(b''.join(_bytes_generator(data)))

        for timer in utool.Timerit(1000, label='inc-generate'):
            hasher = hashlib.sha256()
            with timer:
                for b in _bytes_generator(data):
                    hasher.update(b)

        for timer in utool.Timerit(1000, label='inc-generate'):
            hasher = hashlib.sha256()
            with timer:
                for b in _bytes_generator(data):
                    hasher.update(b)

        for timer in utool.Timerit(1000, label='chunk-inc-generate'):
            hasher = hashlib.sha256()
            import ubelt as ub
            with timer:
                for chunk in ub.chunks(_bytes_generator(data), 5):
                    hasher.update(b''.join(chunk))

        for timer in utool.Timerit(1000, label='inc-update'):
            hasher = hashlib.sha256()
            with timer:
                _update_hasher(hasher, data)

        data = ut.lorium_ipsum()
        hash_data(data)
        ut.hashstr27(data)
        %timeit hash_data(data)
        %timeit ut.hashstr27(repr(data))

        for timer in utool.Timerit(100, label='twocall'):
            hasher = hashlib.sha256()
            with timer:
                hash_data(data)

        hasher = hashlib.sha256()
        hasher.update(memoryview(np.array([1])))
        print(hasher.hexdigest())

        hasher = hashlib.sha256()
        hasher.update(np.array(['1'], dtype=object))
        print(hasher.hexdigest())

    """
    if isinstance(data, (tuple, list, zip)):
        needs_iteration = True
    elif (util_type.HAVE_NUMPY and isinstance(data, np.ndarray) and
          data.dtype.kind == 'O'):
        # ndarrays of objects cannot be hashed directly.
        needs_iteration = True
    else:
        needs_iteration = False

    if needs_iteration:
        # try to nest quickly without recursive calls
        SEP = b'SEP'
        iter_prefix = b'ITER'
        # if isinstance(data, tuple):
        #     iter_prefix = b'TUP'
        # else:
        #     iter_prefix = b'LIST'
        iter_ = iter(data)
        hasher.update(iter_prefix)
        try:
            for item in iter_:
                prefix, hashable = _covert_to_hashable(data)
                binary_data = SEP + prefix + hashable
                # b''.join([SEP, prefix, hashable])
                hasher.update(binary_data)
        except TypeError:
            # need to use recursive calls
            # Update based on current item
            _update_hasher(hasher, item)
            for item in iter_:
                # Ensure the items have a spacer between them
                hasher.update(SEP)
                _update_hasher(hasher, item)
    else:
        prefix, hashable = _covert_to_hashable(data)
        binary_data = prefix + hashable
        # b''.join([prefix, hashable])
        hasher.update(binary_data)