**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/xcom_arg.py
"""
    Class that represents a XCom push from a previous operator.
    Defaults to "return_value" as only key.
    Current implementation supports
        xcomarg >> op
        xcomarg << op
        op >> xcomarg   (by BaseOperator code)
        op << xcomarg   (by BaseOperator code)
    **Example**: The moment you get a result from any operator (functional or regular) you can ::
        any_op = AnyOperator()
        xcomarg = XComArg(any_op)
        # or equivalently
        xcomarg = any_op.output
        my_op = MyOperator()
        my_op >> xcomarg
    This object can be used in legacy Operators via Jinja.
    **Example**: You can make this result to be part of any generated string ::
        any_op = AnyOperator()
        xcomarg = any_op.output
        op1 = MyOperator(my_text_message=f"the value is {xcomarg}")
        op2 = MyOperator(my_text_message=f"the value is {xcomarg['topic']}")
    :param operator: operator to which the XComArg belongs to
    :type operator: airflow.models.baseoperator.BaseOperator
    :param key: key value which is used for xcom_pull (key in the XCom table)
    :type key: str
    """
    def __init__(self, operator: BaseOperator, key: str = XCOM_RETURN_KEY):
        self._operator = operator
        self._key = key
    def __eq__(self, other):