### Apache Airflow version

2.3.0

### What happened

When using a `PythonVirtualenvOperator` it is checked if `virtualenv` is installed by 
`if not shutil.which("virtualenv"):`
https://github.com/apache/airflow/blob/a1679be85aa49c0d6a7ba2c31acb519a5bcdf594/airflow/operators/python.py#L398
Actually, this expression checks if `virtualenv` is on PATH. If Airflow is installed in a virtual environment itself and `virtualenv` is not installed in the environment the check might pass but `virtualenv` cannot be used as it is not present in the environment.

### What you think should happen instead

It should be checked if `virtualenv` is actually available in the environment.

```python
if importlib.util.find_spec("virtualenv") is None:
    raise AirflowException('PythonVirtualenvOperator requires virtualenv, please install it.')
```
https://stackoverflow.com/a/14050282

### How to reproduce

_No response_

### Operating System

Ubuntu 20.04

### Versions of Apache Airflow Providers

_No response_

### Deployment

Virtualenv installation

### Deployment details

_No response_

### Anything else

_No response_

### Are you willing to submit PR?

- [X] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

def my_python_callable(**kwargs):
           ti = kwargs["ti"]
           next_ds = kwargs["next_ds"]
    :param python_callable: A reference to an object that is callable
    :param op_kwargs: a dictionary of keyword arguments that will get unpacked
        in your function
    :param op_args: a list of positional arguments that will get unpacked when
        calling your callable
    :param templates_dict: a dictionary where the values are templates that
        will get templated by the Airflow engine sometime between
        ``__init__`` and ``execute`` takes place and are made available
        in your callable's context after the template has been applied. (templated)
    :param templates_exts: a list of file extensions to resolve while
        processing templated fields, for examples ``['.sql', '.hql']``
    :param show_return_value_in_logs: a bool value whether to show return_value
        logs. Defaults to True, which allows return value log output.
        It can be set to False to prevent log output of return value when you return huge data
        such as transmission a large amount of XCom to TaskAPI.
    """
    template_fields: Sequence[str] = ("templates_dict", "op_args", "op_kwargs")
    template_fields_renderers = {"templates_dict": "json", "op_args": "py", "op_kwargs": "py"}
    BLUE = "#ffefeb"
    ui_color = BLUE
    
    
    shallow_copy_attrs: Sequence[str] = (
        "python_callable",
        "op_kwargs",
    )
    def __init__(