**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
if isinstance(arg, XComArg):
                self.set_upstream(arg.operator)
            elif isinstance(arg, (tuple, set, list)):
                for elem in arg:
                    apply_set_upstream(elem)
            elif isinstance(arg, dict):
                for elem in arg.values():
                    apply_set_upstream(elem)
            elif hasattr(arg, "template_fields"):
                for elem in arg.template_fields:
                    apply_set_upstream(elem)
        for field in self.template_fields:
            if hasattr(self, field):
                arg = getattr(self, field)
                apply_set_upstream(arg)
    @property
    def priority_weight_total(self) -> int:
        """
        Total priority weight for the task. It might include all upstream or downstream tasks.
        depending on the weight rule.
          - WeightRule.ABSOLUTE - only own weight
          - WeightRule.DOWNSTREAM - adds priority weight of all downstream tasks
          - WeightRule.UPSTREAM - adds priority weight of all upstream tasks
        """
        if self.weight_rule == WeightRule.ABSOLUTE:
            return self.priority_weight
        elif self.weight_rule == WeightRule.DOWNSTREAM:
            upstream = False
        elif self.weight_rule == WeightRule.UPSTREAM: