Define Symbol.__slots__ and Rational.__slots__
[linpy.git] / linpy / linexprs.py
index e4ed1cc..c100698 100644 (file)
@@ -273,9 +273,9 @@ class LinExpr:
         Return the expression multiplied by its lowest common denominator to
         make all values integer.
         """
-        lcm = functools.reduce(lambda a, b: a*b // gcd(a, b),
+        lcd = functools.reduce(lambda a, b: a*b // gcd(a, b),
             [value.denominator for value in self.values()])
-        return self * lcm
+        return self * lcd
 
     def subs(self, symbol, expression=None):
         """
@@ -294,21 +294,16 @@ class LinExpr:
         2*x + y + 1
         """
         if expression is None:
-            if isinstance(symbol, Mapping):
-                symbol = symbol.items()
-            substitutions = symbol
+            substitutions = dict(symbol)
         else:
-            substitutions = [(symbol, expression)]
-        result = self
-        for symbol, expression in substitutions:
+            substitutions = {symbol: expression}
+        for symbol in substitutions:
             if not isinstance(symbol, Symbol):
                 raise TypeError('symbols must be Symbol instances')
-            coefficients = [(othersymbol, coefficient)
-                for othersymbol, coefficient in result._coefficients.items()
-                if othersymbol != symbol]
-            coefficient = result._coefficients.get(symbol, 0)
-            constant = result._constant
-            result = LinExpr(coefficients, constant) + coefficient*expression
+        result = self._constant
+        for symbol, coefficient in self._coefficients.items():
+            expression = substitutions.get(symbol, symbol)
+            result += coefficient * expression
         return result
 
     @classmethod
@@ -336,7 +331,7 @@ class LinExpr:
                 return left / right
         raise SyntaxError('invalid syntax')
 
-    _RE_NUM_VAR = re.compile(r'(\d+|\))\s*([^\W\d_]\w*|\()')
+    _RE_NUM_VAR = re.compile(r'(\d+|\))\s*([^\W\d]\w*|\()')
 
     @classmethod
     def fromstring(cls, string):
@@ -455,6 +450,13 @@ class Symbol(LinExpr):
     Two instances of Symbol are equal if they have the same name.
     """
 
+    __slots__ = (
+        '_name',
+        '_constant',
+        '_symbols',
+        '_dimension',
+    )
+
     def __new__(cls, name):
         """
         Return a symbol with the name string given in argument.
@@ -468,12 +470,15 @@ class Symbol(LinExpr):
             raise SyntaxError('invalid syntax')
         self = object().__new__(cls)
         self._name = name
-        self._coefficients = {self: Fraction(1)}
         self._constant = Fraction(0)
         self._symbols = (self,)
         self._dimension = 1
         return self
 
+    @property
+    def _coefficients(self):
+        return {self: Fraction(1)}
+
     @property
     def name(self):
         """
@@ -558,15 +563,8 @@ class Dummy(Symbol):
         """
         if name is None:
             name = 'Dummy_{}'.format(Dummy._count)
-        elif not isinstance(name, str):
-            raise TypeError('name must be a string')
-        self = object().__new__(cls)
+        self = super().__new__(cls, name)
         self._index = Dummy._count
-        self._name = name.strip()
-        self._coefficients = {self: Fraction(1)}
-        self._constant = Fraction(0)
-        self._symbols = (self,)
-        self._dimension = 1
         Dummy._count += 1
         return self
 
@@ -591,6 +589,13 @@ class Rational(LinExpr, Fraction):
     fractions.Fraction classes.
     """
 
+    __slots__ = (
+        '_coefficients',
+        '_constant',
+        '_symbols',
+        '_dimension',
+    ) + Fraction.__slots__
+
     def __new__(cls, numerator=0, denominator=None):
         self = object().__new__(cls)
         self._coefficients = {}