7

I am trying to create a class called PolyExt which is an extension of the Poly class in SymPy. And, it has its own __init__ method. But the problem is that when it passes through the __new__ method in the inherited Poly class, the extra arguments that I added to the __init__ method get interpreted as part of the *gens argument. Is there an elegant way around this?

Here is how to reproduce the error:

class PolyExt(Poly):

    def __init__(self, expression, symb1, symb2):
        self.symb1 = symb1
        self.symb2 = symb2
        super(PolyExt, self).__init__(expression)

x = symbols('x')
y = symbols('y')

PolyExt(x+y, [y], [x])

And, the Poly class looks like the following:

class Poly(Expr):
    """Generic class for representing polynomial expressions."""

    __slots__ = ['rep', 'gens']

    is_commutative = True
    is_Poly = True

    def __new__(cls, rep, *gens, **args):
        """Create a new polynomial instance out of something useful. """
        opt = options.build_options(gens, args)

I tried using keyword arguments for symb1, and symb2 but again, the __new__ method of the Poly class gets in the way, just try the code below

class PolyExt(Poly):

    def __init__(self, expression, **kwargs):
        self.symb1 = kwargs['symb1']
        self.symb2 = kwargs['symb2']
        super(PolyExt, self).__init__(expression)

x = symbols('x')
y = symbols('y')

PolyExt(x+y, symb1=[y], symb2=[x])

What can I do to get around this? Do I need to write my own __new__ method to override the one in class PolyExt? Or is it something else I’m missing?

Thank you.

makansij
  • 9,303
  • 37
  • 105
  • 183

1 Answers1

3

You'll need to use __new__ as well:

from sympy import *
from sympy.abc import x, y

class PolyExt(Poly):

    def __new__(self, expression, symb1, symb2):
        obj = Poly.__new__(self, expression)
        obj.symb1 = symb1
        obj.symb2 = symb2
        return obj

x = symbols('x')
y = symbols('y')

p = PolyExt(x+y, [y], [x])
print(p.symb1)
Oscar Benjamin
  • 12,649
  • 1
  • 12
  • 14
  • Thanks. I have a follow-up question. What determines whether I need to override the `__new__` method? I have a similar post [here](https://stackoverflow.com/questions/57333942/how-to-assign-properties-to-symbols-in-sympy-and-have-them-in-the-same-domain), in which I extended the `Symbol` class, but it did NOT require me to override the `__new__` method, even though the arguments are different. `class Symbol` has `def __new__(cls, name, **assumptions)`, and my extended class (see the question) has an extra argument: `def __init__(self, name, costate)`. – makansij Aug 15 '19 at 21:49
  • 1
    `__new__` is called to *construct* an object and `__init__` is called later to initialise it. The argument you pass to `PolyExt(...)` are received first by `Poly.__new__` which will interpret them as `*gens` because that is its signature. Symbol doesn't expect args but it allows kwargs and in the other post you used your boolean_attr as a keyword argument. If you pass a positional argument it will raise. – Oscar Benjamin Aug 16 '19 at 22:15
  • Ah, excuse me for not realizing `*gens` was a positional argument! Obviously that's key here. thanks @OscarBenjamin. – makansij Aug 19 '19 at 18:48