Index: Lib/decimal.py =================================================================== --- Lib/decimal.py (revision 47089) +++ Lib/decimal.py (working copy) @@ -485,73 +485,78 @@ self = object.__new__(cls) self._is_special = False + _isinstance = isinstance + # Avoid calls to isinstance if possible + # This speeds construction up by a good 20-30% + _FROM_WORK_REP = 1 + _FROM_DECIMAL = 2 + _FROM_INTEGER = 3 + _FROM_SEQ_REP = 4 + _FROM_STRING = 5 + # Do the fast checks for builtin types first + value_cls = value.__class__ + if value_cls is _WorkRep: + path = _FROM_WORK_REP + elif value_cls is Decimal: + path = _FROM_DECIMAL + elif value_cls is str or value_cls is unicode: + path = _FROM_STRING + elif value_cls is int or value_cls is long: + path = _FROM_INTEGER + elif value_cls is tuple or value_cls is list: + path = _FROM_SEQ_REP + else: + # Fast checks all failed, try slow checks + if value_cls is float or _isinstance(value, float): + raise TypeError("Cannot convert float to Decimal. " + + "First convert the float to a string") + elif _isinstance(value, Decimal): + path = _FROM_DECIMAL + elif _isinstance(value, basestring): + path = _FROM_STRING + elif _isinstance(value, (int, long)): + path = _FROM_INTEGER + elif _isinstance(value, (tuple, list)): + path = _FROM_SEQ_REP + else: + path = None + + # From an internal working value - if isinstance(value, _WorkRep): + if path == _FROM_WORK_REP: self._sign = value.sign self._int = tuple(map(int, str(value.int))) self._exp = int(value.exp) return self # From another decimal - if isinstance(value, Decimal): + if path == _FROM_DECIMAL: self._exp = value._exp self._sign = value._sign self._int = value._int self._is_special = value._is_special return self - # From an integer - if isinstance(value, (int,long)): - if value >= 0: - self._sign = 0 - else: - self._sign = 1 - self._exp = 0 - self._int = tuple(map(int, str(abs(value)))) - return self - - # tuple/list conversion (possibly from as_tuple()) - if isinstance(value, (list,tuple)): - if len(value) != 3: - raise ValueError, 'Invalid arguments' - if value[0] not in (0,1): - raise ValueError, 'Invalid sign' - for digit in value[1]: - if not isinstance(digit, (int,long)) or digit < 0: - raise ValueError, "The second value in the tuple must be composed of non negative integer elements." - - self._sign = value[0] - self._int = tuple(value[1]) - if value[2] in ('F','n','N'): - self._exp = value[2] - self._is_special = True - else: - self._exp = int(value[2]) - return self - - if isinstance(value, float): - raise TypeError("Cannot convert float to Decimal. " + - "First convert the float to a string") - - # Other argument types may require the context during interpretation - if context is None: - context = getcontext() - # From a string # REs insist on real strings, so we can too. - if isinstance(value, basestring): - if _isinfinity(value): + if path == _FROM_STRING: + # Need the current context during interpretation + if context is None: + context = getcontext() + is_inf = _isinfinity(value) + if is_inf: self._exp = 'F' self._int = (0,) self._is_special = True - if _isinfinity(value) == 1: + if is_inf == 1: self._sign = 0 else: self._sign = 1 return self - if _isnan(value): - sig, sign, diag = _isnan(value) + is_nan = _isnan(value) + if is_nan: + sig, sign, diag = is_nan self._is_special = True if len(diag) > context.prec: #Diagnostic info too long self._sign, self._int, self._exp = \ @@ -571,6 +576,38 @@ self._sign, self._int, self._exp = context._raise_error(ConversionSyntax) return self + # From an integer + if path == _FROM_INTEGER: + if value >= 0: + self._sign = 0 + else: + self._sign = 1 + self._exp = 0 + self._int = tuple(map(int, str(abs(value)))) + return self + + # tuple/list conversion (possibly from as_tuple()) + if path == _FROM_SEQ_REP: + if len(value) != 3: + raise ValueError, 'Invalid arguments' + if value[0] not in (0,1): + raise ValueError, 'Invalid sign' + for digit in value[1]: + # Avoid call to isinstance if possible + digit_cls = digit.__class__ + is_digit = digit_cls is int or digit_cls is long or _isinstance(digit, (int,long)) + if not is_digit or digit < 0: + raise ValueError, "The second value in the tuple must be composed of non negative integer elements." + + self._sign = value[0] + self._int = tuple(value[1]) + if value[2] in ('F','n','N'): + self._exp = value[2] + self._is_special = True + else: + self._exp = int(value[2]) + return self + raise TypeError("Cannot convert %r to Decimal" % value) def _isnan(self): @@ -2846,18 +2883,30 @@ self.sign = None self.int = 0 self.exp = None - elif isinstance(value, Decimal): - self.sign = value._sign - cum = 0 - for digit in value._int: - cum = cum * 10 + digit - self.int = cum - self.exp = value._exp else: - # assert isinstance(value, tuple) - self.sign = value[0] - self.int = value[1] - self.exp = value[2] + # Avoid calls to isinstance if possible + # This speeds construction up enormously + # Do the fast checks for builtin types first + value_cls = value.__class__ + if value_cls is Decimal: + from_decimal = True + elif value_cls is tuple or value_cls is list: + from_decimal = False + elif isinstance(value, Decimal): + from_decimal = True + else: + from_decimal = False + if from_decimal: + self.sign = value._sign + cum = 0 + for digit in value._int: + cum = cum * 10 + digit + self.int = cum + self.exp = value._exp + else: + self.sign = value[0] + self.int = value[1] + self.exp = value[2] def __repr__(self): return "(%r, %r, %r)" % (self.sign, self.int, self.exp)