Repository: sympy/sympy

Issue:
printer.py in sympy 1.2 seems to break subclassing from `Symbol`
I maintain a project which depends heavily on `sympy`, called [`symfit`](https://github.com/tBuLi/symfit).

However, it seems that sympy 1.2 breaks this project because of the introduction of `_print_Variable` in `codeprinter.py`.

The problem seems to be caused by this: I essentially have an object called `Variable` which inherits from `sympy.Symbol`:
```python
class Variable(Symbol):
    pass
```
When calling `lambdify` on an expression containing this object, we end up (wrongfully) calling
```python
def _print_Variable(self, expr):
    return self._print(expr.symbol)
```
which in turn (rightfully) raises `AttributeError: 'Variable' object has no attribute 'symbol'`.

Before `sympy 1.2` my code wasn't experiencing this problem yet because `_print_Variable` did not exist. It seems to me the way the printing infrastructure is implemented here breaks duck typing, or has `Variable` always been a protected name and should I never have used it? 

The reason I think `sympy` breaks duck typing is because it seems to assume [here](https://github.com/sympy/sympy/blob/c96a971289a25014fc9454e82dffc40156238c3d/sympy/printing/printer.py#L272) that all of these objects are fundamental `sympy` types, which is not the case in this example. If probed again, this `Variable` would tell you that it is actually a `Symbol`. (Actually, the MRO in this case will also contain `Symbol`, whereas the [actual `Variable` from `sympy` ](https://github.com/sympy/sympy/blob/03268800330a27da9e43ebba9eb58c0b00683261/sympy/codegen/ast.py#L1314)does not inherit from `Symbol`.)

A workaround I see [here](https://github.com/sympy/sympy/blob/c96a971289a25014fc9454e82dffc40156238c3d/sympy/printing/printer.py#L264) is to make my `Variable` implement an explicit `self.printmethod` pointing to that of `symbol`, or maybe this should be solved in a more fundamental way?

A full traceback of the error this causes in `symfit` can be found here: tbuli/symfit#169. Ultimately, should this be resolved by redefining my API or is this an issue in `sympy`?

Discussion:
sympy-bot: :white_check_mark:

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot) (v134). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).



* No release notes entry will be added for this pull request.

Note: This comment will be updated with the latest check if you edit the pull request. You need to reload the page to see it. <details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- Your title above should be a short description of what
    was changed. Do not include the issue number in the title. -->

    #### References to other Issues or PRs
    Fixes #14911 


    #### Brief description of what is fixed or changed
    In CodePrinter.doprint the sets `_not_supported` and `_number_symbols` were deleted at the end of this recursive function, leading to an AttributeError for certain Symbol subclasses.

    #### Other comments

    #### Release Notes

    <!-- Write the release notes for this release below. See
    https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
    on how to write release notes. The bot will check your release notes
    automatically to see if they are formatted correctly. -->

    <!-- BEGIN RELEASE NOTES -->
    NO ENTRY
    <!-- END RELEASE NOTES -->


</details><p>

asmeurer: Looks like you pushed some unrelated commits, probably due to a bad rebase. 
asmeurer: Looking back at the issue, I think this fix is OK. We should still consider whether we want to do the other fix (making SymPy printing methods not dispatch to non-SymPy objects). 

+1 once you clean up the commits. 
tBuLi: Yeah I made a last minute rebase after opening the PR and apparently there were some changes since then or whatever. I'll redo the rebase to clean up the history.

And I agree, the other issue is still worth considering. Do you know btw why objects like `Symbol` do not have `_sympystr`, `_lambdacode`, etc. defined on them but rather in the Printer itself? Is it purely not to clutter the code? Because that seems like the easiest solution.

Patch:
diff --git a/sympy/printing/codeprinter.py b/sympy/printing/codeprinter.py
index 363a5c42c9..44e06d8dd1 100644
--- a/sympy/printing/codeprinter.py
+++ b/sympy/printing/codeprinter.py
@@ -117,8 +117,8 @@ def doprint(self, expr, assign_to=None):
             lines = self._format_code(lines)
             num_syms = set([(k, self._print(v)) for k, v in self._number_symbols])
             result = (num_syms, self._not_supported, "\n".join(lines))
-        del self._not_supported
-        del self._number_symbols
+        self._not_supported = set()
+        self._number_symbols = set()
         return result
 
     def _doprint_loops(self, expr, assign_to=None):


Test patch:
diff --git a/sympy/utilities/tests/test_lambdify.py b/sympy/utilities/tests/test_lambdify.py
index 31fb68fda8..07500a9e97 100644
--- a/sympy/utilities/tests/test_lambdify.py
+++ b/sympy/utilities/tests/test_lambdify.py
@@ -839,6 +839,19 @@ def test_issue_12092():
     f = implemented_function('f', lambda x: x**2)
     assert f(f(2)).evalf() == Float(16)
 
+def test_issue_14911():
+    class Variable(sympy.Symbol):
+        def _sympystr(self, printer):
+            return printer.doprint(self.name)
+
+        _lambdacode = _sympystr
+        _numpycode = _sympystr
+
+    x = Variable('x')
+    y = 2 * x
+    code = LambdaPrinter().doprint(y)
+    assert code.replace(' ', '') == '2*x'
+
 def test_ITE():
     assert lambdify((x, y, z), ITE(x, y, z))(True, 5, 3) == 5
     assert lambdify((x, y, z), ITE(x, y, z))(False, 5, 3) == 3