Package sympycore :: Module expr
[hide private]
[frames] | no frames]

Source Code for Module sympycore.expr

  1  # -*- coding: latin-1 -*- 
  2  """ 
  3    This module implements extension type Expr that holds two Python 
  4    objects, head and data, in a pair attribute. 
  5   
  6    When adding new features to Expr class, make sure that these are 
  7    added also to extension type Expr in src/expr_ext.c. 
  8   
  9  C Expr: 
 10   
 11  >>> from sympycore.expr_ext import * 
 12  >>> %timeit Expr(1,2) 
 13  10000000 loops, best of 3: 179 ns per loop 
 14  >>> e=Expr(1,2) 
 15  >>> %timeit h = e.head 
 16  10000000 loops, best of 3: 113 ns per loop 
 17  >>> %timeit h,d = e.pair 
 18  10000000 loops, best of 3: 141 ns per loop 
 19   
 20  Python Expr: 
 21   
 22  >>> from sympycore.expr import * 
 23  >>> %timeit Expr(1,2) 
 24  1000000 loops, best of 3: 988 ns per loop 
 25  >>> e=Expr(1,2) 
 26  >>> %timeit h = e.head 
 27  10000000 loops, best of 3: 119 ns per loop 
 28  >>> %timeit h,d = e.pair 
 29  10000000 loops, best of 3: 139 ns per loop 
 30  """ 
 31  # Author: Pearu Peterson 
 32  # Created: March 2008 
 33   
 34  __all__ = ['Expr'] 
 35   
 36  rc_switch = [] 
 37   
38 -class Expr(object):
39 """Represents an symbolic expression in a pair form: (head, data) 40 41 The pair (head, data) is saved in an attribute ``pair``. The parts of 42 a pair, head and data, can be also accessed via ``head`` and ``data`` 43 attributes, respectively. All three attributes are read-only. 44 45 The head part is assumed to be an immutable object. The data part can 46 be either an immutable object or Python dictionary. In the former 47 case, the hash value of Expr instance is defined as:: 48 49 hash((<Expr>.head, <Expr>. 50 51 Otherwise, if ``data`` contains a Python dictionary, then the hash 52 value is defined as:: 53 54 hash((<Expr>.head, frozenset(<Expr>.data.items()) 55 56 WARNING: the hash value of an Expr instance is computed (and cached) 57 when it is used as a key to Python dictionary. This means that the 58 instance content MUST NOT be changed after the hash is computed. To 59 check if it is safe to change the ``data`` dictionary, use 60 ``is_writable`` attribute that returns True when the hash value has 61 not been computed:: 62 63 <Expr>.is_writable -> True or False 64 65 There are two ways to access the parts of a Expr instance from 66 Python:: 67 68 a = Expr(<head>, <data>) 69 head, data = a.head, a.data - for backward compatibility 70 head, data = a.pair - fastest way 71 72 When Expr constructor is called with one argument, say ``x``, then 73 ``<Expr subclass>.convert(x)`` will be returned. 74 75 This is Python version of Expr type. 76 """ 77 78 __slots__ = ['head', 'data', 'pair', '_hash'] 79
80 - def __init__(self, head, data=None):
81 if data is None: 82 obj = self.convert(head) 83 self.pair = obj.pair 84 self._hash = obj._hash 85 else: 86 self.pair = (head, data) 87 self._hash = None
88
89 - def __repr__(self):
90 return '%s%r' % (type(self).__name__, self.pair)
91
92 - def __hash__(self):
93 """ Compute hash value. 94 95 Different from expr_ext.Expr, an exception is raised when data 96 dictionary values contain dictionaries. 97 """ 98 h = self._hash 99 if not h: 100 pair = self.pair 101 obj = self.as_lowlevel() 102 if obj is not pair: 103 h = hash(obj) 104 else: 105 head, data = pair = self.pair 106 if type(data) is dict: 107 h = hash((head, frozenset(data.iteritems()))) 108 else: 109 h = hash(pair) 110 self._hash = h 111 return h
112 113 @property
114 - def is_writable(self):
115 return not self._hash
116 117 @property
118 - def head(self):
119 return self.pair[0]
120 121 @property
122 - def data(self):
123 return self.pair[1]
124 125 # Pickle support:
126 - def _sethash(self, hashvalue):
127 """ Set hash value for the object. 128 129 If hashvalue==-1, then the hash value will be reset. 130 131 Used by pickle support in sympycore.core._reconstruct. DO NOT 132 use this method directly. 133 """ 134 if hashvalue==-1: 135 self._hash = None 136 else: 137 self._hash = hashvalue
138
139 - def __reduce__(self):
140 version = 1 141 from sympycore.core import _reconstruct 142 if version==1: 143 hashvalue = self._hash 144 if hashvalue is None: 145 hashvalue = -1 146 state = (type(self), self.pair, hashvalue) 147 else: 148 raise NotImplementedError('pickle state version %s' % (version)) 149 return _reconstruct, (version, state)
150 151
152 - def as_lowlevel(self):
153 """ Return self as low-level object instance that will be 154 used in comparison and in hash computation. 155 156 By default. as_lowlevel returns ``data`` part if the ``head`` 157 part is ``SYMBOL`` or ``NUMBER``. Otherwise it returns 158 ``pair`` tuple. Note that returning a copy of the tuple will 159 disable using ``frozenset`` hash algorithm for dictionaries. 160 """ 161 head, data = pair = self.pair 162 if head is NUMBER or head is SYMBOL: 163 return data 164 return pair
165
166 - def __nonzero__(self):
167 return not not self.data
168
169 - def __eq__(self, other):
170 pair = self.pair 171 tother = type(other) 172 if tother is not type(self): 173 obj = self.as_lowlevel() 174 if obj is not pair or type(obj) is tother: 175 return obj == other 176 return False # because types are different 177 return pair==other.pair
178
179 - def __ne__(self, other):
180 return not (self==other)
181
182 - def __lt__(self, other):
183 pair = self.pair 184 tother = type(other) 185 if tother is not type(self): 186 obj = self.as_lowlevel() 187 if obj is not pair or type(obj) is tother: 188 return obj < other 189 return NotImplemented # because types are different 190 return pair < other.pair
191
192 - def __le__(self, other):
193 pair = self.pair 194 tother = type(other) 195 if tother is not type(self): 196 obj = self.as_lowlevel() 197 if obj is not pair or type(obj) is tother: 198 return obj <= other 199 return NotImplemented # because types are different 200 return pair <= other.pair
201
202 - def __gt__(self, other):
203 pair = self.pair 204 tother = type(other) 205 if tother is not type(self): 206 obj = self.as_lowlevel() 207 if obj is not pair or type(obj) is tother: 208 return obj > other 209 return NotImplemented # because types are different 210 return pair > other.pair
211
212 - def __ge__(self, other):
213 pair = self.pair 214 tother = type(other) 215 if tother is not type(self): 216 obj = self.as_lowlevel() 217 if obj is not pair or type(obj) is tother: 218 return obj >= other 219 return NotImplemented # because types are different 220 return pair >= other.pair
221 222 223 from .utils import NUMBER, SYMBOL 224