Source code for srange.symrange

#!/usr/bin/env python
#
# symrange.py
#
# $Id:    $
# $URL: $
#
# Part of the "pydiffract" package
#

import sys

__version__	=	"$Revision: $"
__author__	=	"Jon Tischler, <tischler@aps.anl.gov>" +\
				"Argonne National Laboratory"
__date__	=	"$Date: $"
__id__		=	"$Id: $"


[docs]class symrange: """ symrange class. This class provides functions to loop symmetrically outward from 0 i.e., 0,1,-1,2,-2,3,-3,... endVal can only be a positive integer or 0 EXAMPLE:: >>> for i in symrange(2): print i prints: 0 1 -1 2 -2 3 -3 >>> for i in symrange(2,True): print i prints: 0 -1 1 -2 2 -3 3 NOTE: symrange can only contain integers. variables and methods that you may be interested in: ==================== =========================================================================================== variables ==================== =========================================================================================== self.endVal the highest value +/- returned, this is always >= 0 self.negativeFirst if True then 0,-1,+1,-2,+2,... Otherwise 0,+1,-1,+2,-2,... self.auto_reset if True (default), then previous is reset to None at each call to __iter__ self.length total number of items in range, you can also get this from len(symrange(n)) or symrange(n).len() self.previous last value returned by the iterator, when previous==None, then a call to next() returns 0 ==================== =========================================================================================== ==================== =========================================================================================== methods ==================== =========================================================================================== next() returns next value, updates previous too last() returns the last number in the range, for self.r="3,5,9-20", self.last() returns 20 len() returns number of points in the range, for self.r="3,5,9-20", self.len() returns 14 first() returns first value of iteration, always returns 0 after(prev) returns value that follows prev, without changing the current point in iteration index(ipnt) return the ipntth number from range, first number is ipnt==0, returns None if ipnt negative or too big, same as symrange(2)[ipnt] val2index(m) returns index into range that corresponds to m. e.g. for r='0,-1,1,-2,2', m=1 returns 2. list(self) returns a list where each element is a value in the range, CAUTION this can make a VERY big list if n is large ==================== =========================================================================================== ===================== ======================== =================================================================== special methods command result using: syr = symrange(5) ===================== ======================== =================================================================== __getitem__(n) print syr[3] 2 __len__() print len(syr) 11 __str__() print str(syr) symrange starting from 0, going to 5, doing positives first, current value = "initialized to start" __repr__() print repr(syr) symrange[endVal=5, negativeFirst=False, previous=None, len=11, auto_reset=True] ===================== ======================== =================================================================== """ def __init__(self, endVal, negativeFirst=False, auto_reset=True): """ Initialize the symrange instance. If negativeFirst is True, then symrange(2) goes: 0 -1 1 -2 2 -3 3, (negatives first) """ try: self.endVal = int(round(endVal)) except: raise TypeError('endVal must be an int >= 0, not %r' % endVal) if self.endVal<0 or type(endVal) is bool: raise ValueError('endVal must be an int >= 0, not %r' % endVal) try: self.negativeFirst = bool(negativeFirst) except: raise TypeError('negativeFirst must be a boolean, not %r' % negativeFirst) try: self.auto_reset = bool(auto_reset) except: raise TypeError("auto_reset must be boolean") self.length = (2 * self.endVal + 1) # number of items in the loop, remember that endVal is positive self.previous = None # flags new iteration, next value will be zero def __iter__(self): """ The class iterator """ if self.auto_reset: self.previous = None # flags new iteration, next value will be zero, the default return self
[docs] def next(self): """ Return the next value in the symrange. """ if self.previous is None: # at start self.previous = 0 elif self.negativeFirst and self.previous<0: self.previous = -(self.previous) # neg first & previous negative -> pos previous elif self.negativeFirst: self.previous = -(self.previous)-1 # neg first & previous positive -> -previous-1 elif self.previous <= 0: self.previous = -(self.previous)+1 # positive first & previous is negative (the <=0 is for item after 0) else: self.previous = -(self.previous) # positive first & previous>=0 if abs(self.previous)>self.endVal: # end of loop raise StopIteration return self.previous
[docs] def last(self): """ Return the value of the last item in the range. This method uses but does not change any internal variables, e.g. no self.xxxx """ if self.negativeFirst: return self.endVal else: return -self.endVal
[docs] def first(self): """ Return the value of the first item in the range. This is just for completeness, it always returns 0 """ return 0
[docs] def after(self, val): """ Return the value of the element that follows after val (which is given). """ try: val = int(val) except: raise TypeError('val = %r is not an integer' % val) if self.negativeFirst and val<0: val = -val # neg first & val negative -> pos val elif self.negativeFirst: val = -val-1 # neg first & val positive -> -val-1 elif val<=0: val = -val+1 # positive first & val negative else: val = -val # positive first & val>=0 if abs(val)>self.endVal: val = None # outside of range return val
[docs] def index(self, n): """ Returns the n-th element from the range, zero based, n==0 is first element. This method uses but does not change any internal variables, e.g. no self.xxxx This functionality also available by symrange(2)[n], which calles __getitem__() below """ try: n = int(n) except: raise TypeError('n = %r is not an integer' % n) if n<0: val = None elif n >= self.length: val = None else: val = (n+1) / 2 # positive value isign = n % 2 if self.negativeFirst and isign: val = -val elif not self.negativeFirst and not isign: val = -val return val
def __getitem__(self, n): """ Return the n-th element in the range. This allows use of the symrange(3)[i] syntax """ return self.index(n)
[docs] def val2index(self, val): """ Return the index into the symrange that produces val. This method uses but does not change any internal variables, e.g. no self.xxxx EXAMPLE:: >>> sr = symrange(4) >>> print sr.val2index(3) 5 """ try: val = int(val) except: raise TypeError('val = %r is not an integer' % val) n = max(2*abs(val)-1, 0) # number before if abs(val) > self.endVal: n = None elif self.negativeFirst and val>0: n += 1 elif not self.negativeFirst and val<0: n += 1 return n
[docs] def list(self): """ Expands the symrange into a standard python list. This method uses but does not change any internal variables, e.g. no self.xxxx EXAMPLE:: >>> print symrange(2).list() [0, 1, -1, 2, -2] CAUTION: The following statement:: >>> symrange(100000).list() will produce a list with 200001 elements! Max list length for a 32 bit system is (2^32 - 1)/2/4 = 536870912 on my computer I get a MemoryError for lengths > 1e8, so limit to 1e7 """ lout = [0] if self.negativeFirst: for i in range(self.endVal): lout.append(-i-1) lout.append(i+1) else: for i in range(self.endVal): lout.append(i+1) lout.append(-i-1) return lout
def __len__(self): """ This allows use of len(symrange(3)) syntax """ return self.length
[docs] def len(self): """ Return the number of items in the symrange. Usage: as symrange(3).len() """ return self.length
def __str__(self): """ Return string value for symrange. """ if self.negativeFirst: sss = 'negatives' else: sss = 'positives' if self.previous is None: current = '"initialized to start"' else: current = str(self.previous) return 'symrange starting from 0, going to %d, doing %s first, current value = %s' % (self.endVal, sss, current) def __repr__(self): """ Return printable representation for a symrange. """ return 'symrange[endVal=%r, negativeFirst=%r, previous=%r, len=%r, auto_reset=%r]' % (self.endVal, self.negativeFirst, self.previous, self.length, self.auto_reset)