Returns printer's representation for expr (as a string)

        The representation is obtained by the following methods:

        1. from the :attr:`cache`
        2. If `expr` is a Sympy object, delegate to the
           :meth:`~sympy.printing.printer.Printer.doprint` method of
           :attr:`_sympy_printer`
        3. Let the `expr` print itself if has the :attr:`printmethod`
        4. Take the best fitting ``_print_*`` method of the printer
        5. As fallback, delegate to :meth:`emptyPrinter`

        Any extra `args` or `kwargs` are passed to the internal `_print`
        method.

def doprint(self, expr, *args, **kwargs):
        """Returns printer's representation for expr (as a string)

        The representation is obtained by the following methods:

        1. from the :attr:`cache`
        2. If `expr` is a Sympy object, delegate to the
           :meth:`~sympy.printing.printer.Printer.doprint` method of
           :attr:`_sympy_printer`
        3. Let the `expr` print itself if has the :attr:`printmethod`
        4. Take the best fitting ``_print_*`` method of the printer
        5. As fallback, delegate to :meth:`emptyPrinter`

        Any extra `args` or `kwargs` are passed to the internal `_print`
        method.
        """
        allow_caching = self._allow_caching
        is_cached = False
        if len(args) > 0 or len(kwargs) > 0:
            # we don't want to cache "custom" rendering, such as the adjoint of
            # the actual expression (kwargs['adjoint'] is True). Otherwise, we
            # might return a cached values for args/kwargs that are different
            # from the the expression was originally cached.
            allow_caching = False

        if allow_caching:
            is_cached, res = self._get_from_cache(expr)
        if not is_cached:
            if isinstance(expr, Scalar._val_types):
                res = self._print_SCALAR_TYPES(expr, *args, **kwargs)
            elif isinstance(expr, str):
                return self._render_str(expr)
            else:
                # the _print method, inherited from SympyPrinter implements the
                # internal dispatcher for (3-5)
                res = self._str(self._print(expr, *args, **kwargs))
            if allow_caching:
                self._write_to_cache(expr, res)
        return res