def visit_versions(self, func):
        """Visit each version in the range, and apply a function to each.

        This is for advanced usage only.

        If `func` returns a `Version`, this call will change the versions in
        place.

        It is possible to change versions in a way that is nonsensical - for
        example setting an upper bound to a smaller version than the lower bound.
        Use at your own risk.

        Args:
            func (callable): Takes a `Version` instance arg, and is applied to
                every version in the range. If `func` returns a `Version`, it
                will replace the existing version, updating this `VersionRange`
                instance in place.
        """
        for bound in self.bounds:
            if bound.lower is not _LowerBound.min:
                result = func(bound.lower.version)
                if isinstance(result, Version):
                    bound.lower.version = result

            if bound.upper is not _UpperBound.inf:
                result = func(bound.upper.version)
                if isinstance(result, Version):
                    bound.upper.version = result
Visit each version in the range, and apply a function to each.

        This is for advanced usage only.

        If `func` returns a `Version`, this call will change the versions in
        place.

        It is possible to change versions in a way that is nonsensical - for
        example setting an upper bound to a smaller version than the lower bound.
        Use at your own risk.

        Args:
            func (callable): Takes a `Version` instance arg, and is applied to
                every version in the range. If `func` returns a `Version`, it
                will replace the existing version, updating this `VersionRange`
                instance in place.