**Description**

Currently `BaseOperator` and `XComArgs` implements the same logic of `__lshift__`, `__rshift__` and others "chain" operations (`>>`, `<<`). It seems resonable to abstract this logic into `TaskMixin` to introduce some DRYness especially because the new concept of `TaskGroup` will also implement the same methods.

**Use case / motivation**

Limit duplication of logic.

**Related Issues**

https://github.com/apache/airflow/pull/10827#issuecomment-691927501
#10153 

airflow/models/baseoperator.py
from airflow.models.xcom_arg import XComArg
        if isinstance(content, str):
            if any(content.endswith(ext) for ext in self.template_ext):
                
                return jinja_env.get_template(content).render(**context)
            else:
                return jinja_env.from_string(content).render(**context)
        elif isinstance(content, XComArg):
            return content.resolve(context)
        if isinstance(content, tuple):
            if type(content) is not tuple:  
                
                return content.__class__(
                    *(self.render_template(element, context, jinja_env) for element in content)  
                )
            else:
                return tuple(self.render_template(element, context, jinja_env) for element in content)
        elif isinstance(content, list):
            return [self.render_template(element, context, jinja_env) for element in content]
        elif isinstance(content, dict):
            return {key: self.render_template(value, context, jinja_env) for key, value in content.items()}
        elif isinstance(content, set):
            return {self.render_template(element, context, jinja_env) for element in content}
        else:
            if seen_oids is None:
                seen_oids = set()
            self._render_nested_template_fields(content, context, jinja_env, seen_oids)
            return content
    def _render_nested_template_fields(