class_docstr: Handler dispatcher for associative operators
  
  .. notes::
     This approach is experimental, and can be replaced or deleted in the future.
     See https://github.com/sympy/sympy/pull/19463.
  
  Explanation
  ===========
  
  If arguments of different types are passed, the classes which handle the operation for each type
  are collected. Then, a class which performs the operation is selected by recursive binary dispatching.
  Dispatching relation can be registered by ``register_handlerclass`` method.
  
  Priority registration is unordered. You cannot make ``A*B`` and ``B*A`` refer to
  different handler classes. All logic dealing with the order of arguments must be implemented
  in the handler class.
  
  Examples
  ========
  
  >>> from sympy import Add, Expr, Symbol
  >>> from sympy.core.add import add
  
  >>> class NewExpr(Expr):
  ...     @property
  ...     def _add_handler(self):
  ...         return NewAdd
  >>> class NewAdd(NewExpr, Add):
  ...     pass
  >>> add.register_handlerclass((Add, NewAdd), NewAdd)
  
  >>> a, b = Symbol('a'), NewExpr()
  >>> add(a, b) == NewAdd(a, b)
  True
docstr: Register the handler class for two classes, in both straight and reversed order.
  
  Paramteters
  ===========
  
  classes : tuple of two types
      Classes who are compared with each other.
  
  typ:
      Class which is registered to represent *cls1* and *cls2*.
      Handler method of *self* must be implemented in this class.
docstr_tok: ["Register","the","handler","class","for","two","classes",",","in","both","straight","and","reversed","order",".","Paramteters","===========","classes",":","tuple","of","two","types","Classes","who","are","compared","with","each","other",".","typ",":","Class","which","is","registered","to","represent","*","cls1","*","and","*","cls2","*",".","Handler","method","of","*","self","*","must","be","implemented","in","this","class","."]
code: def register_handlerclass(
      self,
      classes,
      typ,
      on_ambiguity=ambiguity_register_error_ignore_dup,
  ):
      """
      Register the handler class for two classes, in both straight and reversed order.
  
      Paramteters
      ===========
  
      classes : tuple of two types
          Classes who are compared with each other.
  
      typ:
          Class which is registered to represent *cls1* and *cls2*.
          Handler method of *self* must be implemented in this class.
      """
      if not len(classes) == 2:
          raise RuntimeError(
              "Only binary dispatch is supported, but got %s types: <%s>."
              % (len(classes), str_signature(classes))
          )
      if len(set(classes)) == 1:
          raise RuntimeError(
              "Duplicate types <%s> cannot be dispatched."
              % str_signature(classes)
          )
      self._dispatcher.add(
          tuple(classes), typ, on_ambiguity=on_ambiguity
      )
      self._dispatcher.add(
          tuple(reversed(classes)), typ, on_ambiguity=on_ambiguity
      )
  
imports: from __future__ import annotations
  from operator import attrgetter
  from collections import defaultdict
  from sympy.utilities.exceptions import sympy_deprecation_warning
  from .sympify import _sympify, sympify
  from .basic import Basic
  from .cache import cacheit
  from .sorting import ordered
  from .logic import fuzzy_and
  from .parameters import global_parameters
  from sympy.utilities.iterables import sift
  from sympy.multipledispatch.dispatcher import Dispatcher, ambiguity_register_error_ignore_dup, str_signature, RaiseNotImplementedError
label_desc: Use image node_id 3 for calling the AssocOpDispatcher obj's underlying member method code with example usage: obj.register_handlerclass(classes, typ, on_ambiguity) without return types