Package MolKit :: Module listSet
[hide private]
[frames] | no frames]

Source Code for Module MolKit.listSet

  1  ############################################################################# 
  2  # 
  3  # Author: Michel F. SANNER 
  4  # 
  5  # Copyright: M. Sanner TSRI 2000 
  6  # 
  7  ############################################################################# 
  8   
  9  # 
 10  # $Header: /opt/cvs/python/packages/share1.5/MolKit/listSet.py,v 1.26 2006/06/12 18:28:35 sargis Exp $ 
 11  # 
 12  # $Id: listSet.py,v 1.26 2006/06/12 18:28:35 sargis Exp $ 
 13  # 
 14   
 15  """ 
 16  This module implements a Set class that uses a List to store the objects 
 17  in the set. 
 18  """ 
 19   
 20  import types 
 21  from UserList import UserList 
 22  from mglutil.util import misc 
 23   
 24  verbose = False 
 25   
26 -class ListSet(UserList): # would (list) work???
27 """Class to represent Sets of objects stored in a list. There is an 28 implicit order amongst the objects and there can be duplicate objects. 29 30 __getattr__, __setattr__ and __delattr__ have been modified to operate on 31 the list of objects rather than the TreeNodeSet itself, i.e. if atm is an 32 instance of a ListSet a.xxx will not return the member xxx of the object 33 atm but rather a list of the members xxx from each object in the set atm. 34 xxx can be a member of a function that requires no argument. 35 36 Example: 37 38 if atm is an instance of a ListSet: 39 atm.name return the name attribute of each Atom in atm 40 atm.newprop = 7.2 creates a newprop attribute for each Atom in atm 41 with an initial value of 7.2 42 atm.newIndex = range(len(atm)) create a newIndex attribute for each Atom 43 in atm with values 0 for the first atom, 1 for the 44 second, etc... 45 del atm.newIndex 46 47 This class also implement boolean operations on ListSets. These operation 48 overload some operators. 49 50 A uniq() method returns a list with the double removed. 51 A makeUnique() method removes duplicates from list (in place). 52 """ 53
54 - def __init__(self, data=None, elementType=None, stringRepr=None, 55 comments="", keywords=[]):
56 if data is not None and len(data)!=0: 57 assert( hasattr(data[0], '__class__') ) 58 if elementType is not None: 59 assert( isinstance(data[0], elementType) ) 60 61 UserList.__init__(self, data) 62 # save call to __setattr__ 63 self.__dict__['elementType'] = elementType 64 #self.elementType = elementType # class of elements in that set 65 self.stringRepr = stringRepr # this will hold a concise string repr of 66 # the set of objects in that list 67 self.__dict__['comments'] = comments 68 self.__dict__['keywords'] = keywords 69 self.selector = None
70 71
72 - def setStringRepr(self, string):
73 """set the string representation of this set""" 74 assert type(string) in types.StringTypes 75 self.stringRepr = string
76 77
78 - def getStringRepr(self):
79 """return the string representation of this set""" 80 return self.stringRepr
81 82
83 - def copy(self):
84 """return a copy of the set""" 85 copy = self.__class__(self.data, stringRepr = self.stringRepr) 86 return copy
87 88
89 - def __str__(self):
90 """add here because __str__ is missing in UserList which creates a pb 91 in jpython""" 92 return str(self.data)
93 94
95 - def __delattr__(self, member):
96 if member[:2]=='__' or member in ['data', 'elementType']: 97 return 98 func = 'if hasattr(o,"%s"): del o.%s' % (member,member) 99 for o in self.data: 100 exec (func)
101 102
103 - def __iter__(self, *cfg, **kw):
104 return iter(self.data)
105 106
107 - def __getattr__(self, member):
108 """Extract the specified member from each objects in the set and 109 returns them as a list 110 """ 111 112 if member[:2]=='__': 113 if self.__dict__.has_key(member): 114 return self.__dict__[member] 115 else: 116 raise AttributeError('member %s not found'%member) 117 elif member in ['data', 'elementType', 'stringRepr','comments', 'keywords', 'selector']: 118 return self.__dict__[member] 119 else: 120 121 result = [] 122 # if len(self.data) and callable( eval('self.data[0].%s' % member )): 123 if len(self.data) and callable( getattr(self.data[0], member) ): 124 m = self.data[0].__class__.__dict__[member] 125 for o in self.data: 126 result.append( m(o) ) 127 # result = map( eval ('lambda x: x.%s()' % member), self.data ) 128 else: 129 for o in self.data: 130 result.append( o.__dict__[member] ) 131 # result = map( eval ('lambda x: x.%s' % member), self.data ) 132 133 return result
134
135 - def getAll(self, member):
136 return self.__getattr__(member)
137
138 - def setAll(self, member, value):
139 return self.__setattr__(member, value)
140
141 - def __setattr__(self, member, value):
142 """Set or create member in each object in this set. 143 If value is a sequence it has to be of the same length as the set. 144 else the new member in each object in the set is set to 'value' 145 """ 146 147 if member[:2]=='__': 148 self.__dict__[member] = value 149 elif member in ['data', 'elementType', 'stringRepr', 'comments', 'keywords', 'selector']: 150 self.__dict__[member] = value 151 else: 152 l = len(self.data) 153 if not misc.issequence(value): # simple number of string 154 #map( setThem, self.data, (value,)*l, (member,)*l ) 155 #for o in self.data: exec ( 'o.%s = value'%member ) 156 for o in self.data: o.__dict__[member] = value 157 158 else: # value is a sequence 159 160 if len(value) == 1: # sequence of 1 .. treat like single number 161 #map( setThem, self.data, value*l, (member,)*l ) 162 #for o in self.data: exec ( 'o.%s = value'%member ) 163 for o in self.data: o.__dict__[member] = value[0] 164 165 else: # sequence of many values 166 assert len(self.data) == len(value) 167 for o,v in map(None, self.data, value): 168 setattr(o, member, v)
169 # for i in xrange(len(self.data)): 170 # self.data[i].__dict__[member] = value[i] 171 172
173 - def append(self, item):
174 assert isinstance(item, self.elementType) 175 if len(self.data)>0 and self.stringRepr and hasattr(item, 'full_name'): 176 self.stringRepr = self.stringRepr+'/+/'+item.full_name() 177 elif hasattr(item, 'full_name'): 178 self.stringRepr = item.full_name() 179 self.data.append(item)
180
181 - def insert(self, i, item):
182 assert isinstance(item, self.elementType) 183 if len(self.data)>0 and self.stringRepr and hasattr(item, 'full_name'): 184 self.stringRepr = self.stringRepr+'/+/'+item.full_name() 185 elif hasattr(item, 'full_name'): 186 self.stringRepr = item.full_name() 187 self.data.insert(i, item)
188
189 - def pop(self, i=-1):
190 assert isinstance(item, self.elementType) 191 elem = self.data.pop(i) 192 if len(self.data)>0 and self.stringRepr and hasattr(item, 'full_name'): 193 self.stringRepr = self.stringRepr+'/-/'+item.full_name() 194 else: 195 self.stringRepr = None 196 return elem
197
198 - def remove(self, item):
199 assert isinstance(item, self.elementType) 200 self.data.remove(item) 201 if len(self.data)>0 and self.stringRepr and hasattr(item, 'full_name'): 202 self.stringRepr = self.stringRepr+'/-/'+item.full_name() 203 else: 204 self.stringRepr = None
205
206 - def __getslice__(self, i, j):
207 to_end = False 208 if j>len(self.data)-1: 209 j=len(self.data) 210 if i==0: to_end = True 211 #else: 212 # j += 1 # %d-%d in selection language includes start and end 213 # while [i:j] excludes j 214 if self.stringRepr: 215 # build a string repr which is the and of current set and 216 # range [i,j] on surrent level 217 stringRepr = self.stringRepr+'/&/' 218 ind = self.stringRepr.rfind(':') 219 if to_end: 220 stringRepr = self.stringRepr 221 elif ind > 0: # we found ':' 222 if self.stringRepr[-1]==':': #nothing beyond last ':' 223 # everything at this level is in the set 224 # we only have to add the range 225 if i<j-1: 226 stringRepr = self.stringRepr[:ind+1]+'%d-%d'%(i,j-1) 227 else: #could be only the last item 228 stringRepr = self.stringRepr[:ind+1]+'%d'%(i) 229 else: 230 # only a subset of the current level is in self 231 # we have to do a sub-selection 232 if i<j-1: 233 stringRepr = self.stringRepr+'\\s\\'+'%d-%d'%(i,j-1) 234 else: #could be only the last item 235 stringRepr = self.stringRepr+'\\s\\'+'%d'%(i) 236 else: # we are the root level 237 stringRepr = self.data[i].name 238 for m in self.data[i+1:j]: 239 stringRepr += ','+m.name 240 else: 241 if verbose: 242 print 'WARNING long stringRepr due to getslice' 243 stringRepr = '' 244 for obj in self.data[i:j]: 245 stringRepr += obj.full_name()+';' 246 stringRepr = stringRepr[:-1] # remove last semi colon 247 return self.__class__(self.data[i:j], stringRepr=stringRepr)
248 249
250 - def __delslice__(self, i, j):
251 if verbose: 252 print 'WARNING long stringRepr due to delslice' 253 del self.data[i:j] 254 stringRepr = '' 255 for obj in self.data: 256 stringRepr += obj.full_name()+';' 257 self.stringRepr = stringRepr[:-1] # remove last semi colon
258 259
260 - def __mul__(self, n):
261 # returns a new set 262 if len(self.data)==0: 263 return self.__class__([]) 264 origStringRepr = self.stringRepr 265 for i in range(i-1): 266 stringRepr += '/+/'+origStringRepr 267 return self.__class__(self.data*n, stringRepr=stringRepr)
268 269
270 - def __imul__(self, n):
271 # multiplies the set in place 272 if len(self.data)==0: 273 return self 274 self.data *= n 275 origStringRepr = self.stringRepr 276 for i in range(i-1): 277 stringRepr += '/+/'+origStringRepr 278 self.stringRepr = stringRepr 279 return self
280 281
282 - def extend(self, right):
283 assert isinstance(right, self.__class__) 284 if len(right.data)==0: return 285 self.data.extend(right.data) 286 if self.stringRepr and right.stringRepr: 287 self.stringRepr = self.stringRepr+'/+/'+right.stringRepr 288 elif verbose: 289 import traceback 290 traceback.print_stack() 291 print 'extending sets with no stringRepr:', repr(self), repr(right)
292 293
294 - def __iadd__(self, right):
295 """See add: overloads += operator""" 296 self.extend(right) 297 return self
298 299
300 - def __add__(self, right):
301 """See add: overloads + operator""" 302 assert isinstance(right, self.__class__) 303 if len(right.data)==0: return self.copy() 304 if len(self.data)==0: return right.copy() 305 stringRepr = None 306 if self.stringRepr and right.stringRepr: 307 stringRepr = self.stringRepr+'/+/'+right.stringRepr 308 elif verbose: 309 import traceback 310 traceback.print_stack() 311 print 'adding sets with no stringRepr:', repr(self), repr(right) 312 stringRepr = None 313 return self.__class__( self.data + right.data, stringRepr=stringRepr)
314 315
316 - def union(self, right):
317 """Union: returns a Set holding objects appearing in either list""" 318 319 assert isinstance(right, self.__class__) 320 stringRepr = None 321 if len(right.data)==0: return self.copy() 322 if len(self.data)==0: return right.copy() 323 if self.stringRepr and right.stringRepr: 324 if self.stringRepr == right.stringRepr: 325 stringRepr = self.stringRepr 326 else: 327 stringRepr = self.stringRepr+'/|/'+right.stringRepr 328 elif verbose: 329 import traceback 330 traceback.print_stack() 331 print 'union of sets with no stringRepr:', repr(self), repr(right) 332 stringRepr = None 333 return self.__class__( misc.uniq(self.data + right.data), 334 stringRepr=stringRepr )
335 336
337 - def __or__(self, right):
338 """See union: overloads | operator""" 339 return self.union(right)
340 341
342 - def xor(self, right):
343 """XOR operation: Returns a set made of the elements appearing in first 344 or second set but not in both""" 345 346 assert isinstance(right, self.__class__) 347 if len(right.data)==0: return self.copy() 348 if len(self.data)==0: return right.copy() 349 stringRepr = None 350 l1 = ListSet.__sub__(self, right) 351 l2 = ListSet.__sub__(right, self) 352 if self.stringRepr and right.stringRepr: 353 stringRepr = self.stringRepr+'/^/'+right.stringRepr 354 elif verbose: 355 import traceback 356 traceback.print_stack() 357 print 'xoring sets with no stringRepr:', repr(self), repr(right) 358 stringRepr = None 359 return self.__class__( l1.data + l2.data, stringRepr=stringRepr )
360 361
362 - def __xor__(self, right):
363 """See union: overloads ^ operator""" 364 return self.xor(right)
365 366
367 - def inter(self, right):
368 """Intersection: returns a Set holding objects appearing in both sets 369 """ 370 371 assert isinstance(right, self.__class__) 372 if len(right.data)==0: return self.copy() 373 if len(self.data)==0: return right.copy() 374 l1 = self 375 l2 = right 376 if len(l1.data) > len(right.data): 377 l1 = right 378 l2 = self 379 # l1 is always shorter list 380 for o in l2.data: o._setFlag = 0 381 for o in l1.data: o._setFlag = 1 382 newlist = filter( lambda x: x._setFlag==1, l2.data ) 383 for o in l2.data: 384 if hasattr(o, '_setFlag'): 385 del o._setFlag 386 for o in l1.data: 387 if hasattr(o, '_setFlag'): 388 del o._setFlag 389 stringRepr = None 390 if self.stringRepr and right.stringRepr: 391 stringRepr = self.stringRepr+'/&/'+right.stringRepr 392 elif verbose: 393 import traceback 394 traceback.print_stack() 395 print 'intersecting sets with no stringRepr:', repr(self), repr(right) 396 stringRepr = None 397 return self.__class__(misc.uniq(newlist), stringRepr=stringRepr)
398 399
400 - def __and__(self, right):
401 """See inter: overloads & operator""" 402 return self.inter(right)
403 404
405 - def subtract(self, right):
406 """Returns a set made of the elements of the first set not appearing 407 in the second set""" 408 409 stringRepr = None 410 assert isinstance(right, self.__class__) 411 if len(right.data)==0: return self.copy() 412 if len(self.data)==0: return self.copy() 413 for o in self.data: o._setFlag = 1 414 for o in right.data: o._setFlag = 0 415 newlist = filter( lambda x: x._setFlag==1, self.data ) 416 for o in self.data: 417 if hasattr(o, '_setFlag'): 418 del o._setFlag 419 for o in right.data: 420 if hasattr(o, '_setFlag'): 421 del o._setFlag 422 if self.stringRepr and right.stringRepr: 423 stringRepr = self.stringRepr+'/-/'+right.stringRepr 424 elif verbose: 425 import traceback 426 traceback.print_stack() 427 print 'subtracting sets with no stringRepr:', repr(self), repr(right) 428 #stringRepr = None 429 return self.__class__(newlist, stringRepr=stringRepr)
430 431
432 - def __sub__(self, right):
433 """See subtract: overloads - operator""" 434 return self.subtract(right)
435 436
437 - def makeUniq(self):
438 """removes duplicates from set (in place)""" 439 l = [] 440 d = {} 441 for value in self.data: 442 if not d.has_key(id(value)): 443 d[id(value)]=value 444 l.append(value) 445 self.__dict__['data'] = l
446 447
448 - def uniq(self): # Fastest order preserving
449 set = {} 450 451 return self.__class__([set.setdefault(e,e) for e in self.data if e not in set]) 452 453 ## def uniq(self): 454 ## l = [] 455 ## d = {} 456 ## for value in self.data: 457 ## # Here we use the id(value) as a key rather than value itself 458 ## # because if self.data is a TreeNodeSet each time 459 ## # we test if d.has_key(value) it calls the __hash__ method 460 ## # of tree.py which has been overwritten 461 ## # Function calls in python are costly. 462 ## if not d.has_key(id(value)): 463 ## d[id(value)]=value 464 ## l.append(value) 465 ## return self.__class__(l) 466