Package topo :: Package param
[hide private]
[frames] | no frames]

Source Code for Package topo.param

  1  """ 
  2  Parameters are a kind of class attribute allowing special behavior, 
  3  including dynamically generated parameter values, documentation 
  4  strings, constant and read-only parameters, and type or range checking 
  5  at assignment time. 
  6   
  7  Potentially useful for any large Python program that needs 
  8  user-modifiable object attributes; see the parameterized.Parameter and 
  9  parameterized.Parameterized classes for more information. 
 10   
 11   
 12  This file contains subclasses of Parameter, implementing specific 
 13  parameter types (e.g. Number). 
 14   
 15  $Id: __init__.py 9414 2008-10-08 10:08:23Z ceball $ 
 16  """ 
 17  __version__='$Revision: 9414 $' 
 18   
 19  # CEBALERT: we'll need more documentation above (eventually, when 
 20  # params becomes a separate directory and then package). 
 21   
 22  import types 
 23   
 24  from parameterized import Parameterized, Parameter, descendents 
 25   
26 -class Enumeration(Parameter):
27 """ 28 Enumeration is a Parameter with a list of available values. 29 30 An Enumeration's value is always one from its list of available values. 31 """ 32 __slots__ = ['available'] 33
34 - def __init__(self, default=None, available=[], **params):
35 """ 36 Create an Enumeration, checking that 'default' is in 'available'. 37 """ 38 try: 39 default in available 40 except TypeError, te: 41 raise TypeError("Enumeration's 'available' argument must be iterable ('%s' is not iterable)."%available) 42 43 Parameter.__init__(self,default=default,**params) 44 self.available = available 45 self.__check_value(default)
46
47 - def __set__(self,obj,val):
48 """ 49 Set to the given value, raising an exception if that value is 50 not in the list of available ones. 51 """ 52 self.__check_value(val) 53 super(Enumeration,self).__set__(obj,val)
54
55 - def __check_value(self,val):
56 """ 57 Raises an error if the given value isn't in the list of available ones. 58 """ 59 if val not in self.available: 60 raise ValueError("%s not in %s's list of available values."%(val,self._attrib_name))
61 62
63 -def produce_value(value_obj):
64 """ 65 A helper function that produces an actual parameter from a stored 66 object: if the object is callable, call it, otherwise return the 67 object. 68 """ 69 if callable(value_obj): 70 return value_obj() 71 else: 72 return value_obj
73 74
75 -class Dynamic(Parameter):
76 """ 77 Parameter whose value can be generated dynamically by a callable 78 object. 79 80 If a Parameter is declared as Dynamic, it can be set a callable 81 object (such as a function or callable class), and getting the 82 parameter's value will call that callable. 83 84 Note that at present, the callable object must allow attributes 85 to be set on itself. 86 87 [Python 2.4 limitation: the callable object must be an instance of a 88 callable class, rather than a named function or a lambda function, 89 otherwise the object will not be picklable or deepcopyable.] 90 91 92 Setting Dynamic.time_fn allows the production of dynamic values to 93 be controlled: a new value will be produced only if the current 94 value of time_fn is greater than what it was last time the 95 parameter value was requested. 96 97 If time_fn is set to None, a new value is always produced. 98 99 If Dynamic.time_fn is set to something other than None, it must, 100 when called, produce a number. 101 """ 102 # CB: making Dynamic support iterators and generators is sf.net 103 # feature request 1864370. When working on that task, note that 104 # detection of a dynamic generator by 'callable' needs to be 105 # replaced by something that matches whatever Dynamic becomes 106 # capable of using. 107 108 time_fn = None # could add a slot for time_fn to allow instances 109 # to override 110 111 # CBENHANCEMENT: Add an 'epsilon' slot. 112 # See email 'Re: simulation-time-controlled Dynamic parameters' 113 # Dec 22, 2007 CB->JAB 114
115 - def __init__(self,**params):
116 """ 117 Call the superclass's __init__ and set instantiate=True if the 118 default is dynamic. 119 """ 120 super(Dynamic,self).__init__(**params) 121 122 if callable(self.default): 123 self._set_instantiate(True) 124 self._initialize_generator(self.default)
125 126
127 - def _initialize_generator(self,gen,obj=None):
128 """ 129 Add 'last time' and 'last value' attributes to the generator. 130 """ 131 # CEBALERT: use a dictionary to hold these things. 132 if hasattr(obj,"_Dynamic_time_fn"): 133 gen._Dynamic_time_fn = obj._Dynamic_time_fn 134 135 gen._Dynamic_last = None 136 # CEB: I'd use None for this, except can't compare a fixedpoint 137 # number with None (e.g. 1>None but FixedPoint(1)>None can't be done) 138 gen._Dynamic_time = -1 139 140 gen._saved_Dynamic_last = [] 141 gen._saved_Dynamic_time = []
142 143
144 - def __get__(self,obj,objtype):
145 """ 146 Call the superclass's __get__; if the result is not dynamic 147 return that result, otherwise ask that result to produce a 148 value and return it. 149 """ 150 gen = super(Dynamic,self).__get__(obj,objtype) 151 152 if not hasattr(gen,'_Dynamic_last'): 153 return gen 154 else: 155 return self._produce_value(gen)
156 157
158 - def __set__(self,obj,val):
159 """ 160 Call the superclass's set and keep this parameter's 161 instantiate value up to date (dynamic parameters 162 must be instantiated). 163 164 If val is dynamic, initialize it as a generator. 165 """ 166 super(Dynamic,self).__set__(obj,val) 167 168 dynamic = callable(val) 169 if dynamic: self._initialize_generator(val,obj) 170 if not obj: self._set_instantiate(dynamic)
171 172
173 - def _produce_value(self,gen,force=False):
174 """ 175 Return a value from gen. 176 177 If there is no time_fn, then a new value will be returned 178 (i.e. gen will be asked to produce a new value). 179 180 If force is True, or the value of time_fn() is greater than 181 what it was was last time produce_value was called, a 182 new value will be produced and returned. Otherwise, 183 the last value gen produced will be returned. 184 """ 185 if hasattr(gen,"_Dynamic_time_fn"): 186 time_fn = gen._Dynamic_time_fn 187 else: 188 time_fn = self.time_fn 189 190 if time_fn is None: 191 value = produce_value(gen) 192 gen._Dynamic_last = value 193 else: 194 195 time = time_fn() 196 197 if force or time>gen._Dynamic_time: 198 value = produce_value(gen) 199 gen._Dynamic_last = value 200 gen._Dynamic_time = time 201 else: 202 value = gen._Dynamic_last 203 204 return value
205 206
207 - def _value_is_dynamic(self,obj,objtype=None):
208 """ 209 Return True if the parameter is actually dynamic (i.e. the 210 value is being generated). 211 """ 212 return hasattr(super(Dynamic,self).__get__(obj,objtype),'_Dynamic_last')
213 214
215 - def _inspect(self,obj,objtype=None):
216 """Return the last generated value for this parameter.""" 217 gen=super(Dynamic,self).__get__(obj,objtype) 218 219 if hasattr(gen,'_Dynamic_last'): 220 return gen._Dynamic_last 221 else: 222 return gen
223 224
225 - def _force(self,obj,objtype=None):
226 """Force a new value to be generated, and return it.""" 227 gen=super(Dynamic,self).__get__(obj,objtype) 228 229 if hasattr(gen,'_Dynamic_last'): 230 return self._produce_value(gen,force=True) 231 else: 232 return gen
233 234 235 236 import operator 237 is_number = operator.isNumberType 238 239 240
241 -class Number(Dynamic):
242 """ 243 Number is a numeric parameter. Numbers have a default value and 244 optional bounds. There are two types of bounds: ``bounds`` and 245 ``softbounds``. ``bounds`` are hard bounds: the parameter must 246 have a value within the specified range. The default bounds are 247 (None,None), meaning there are actually no hard bounds. One or 248 both bounds can be set by specifying a value 249 (e.g. bounds=(None,10) means there is no lower bound, and an upper 250 bound of 10). 251 252 Number is also a type of Dynamic parameter, so its value 253 can be set to a callable to get a dynamically generated 254 number (see Dynamic). 255 256 When not being dynamically generated, bounds are checked when a 257 Number is created or set. Using a default value outside the hard 258 bounds, or one that is not numeric, results in an exception. When 259 being dynamically generated, bounds are checked when a the value 260 of a Number is requested. A generated value that is not numeric, 261 or is outside the hard bounds, results in an exception. 262 263 As a special case, if allow_None=True (which is true by default if 264 the parameter has a default of None when declared) then a value 265 of None is also allowed. 266 267 A separate function set_in_bounds() is provided that will 268 silently crop the given value into the legal range, for use 269 in, for instance, a GUI. 270 271 ``softbounds`` are present to indicate the typical range of 272 the parameter, but are not enforced. Setting the soft bounds 273 allows, for instance, a GUI to know what values to display on 274 sliders for the Number. 275 276 Example of creating a Number:: 277 AB = Number(default=0.5, bounds=(None,10), softbounds=(0,1), doc='Distance from A to B.') 278 """ 279 __slots__ = ['bounds','_softbounds','allow_None'] 280
281 - def __init__(self,default=0.0,bounds=None,softbounds=None,allow_None=False,**params):
282 """ 283 Initialize this parameter object and store the bounds. 284 285 Non-dynamic default values are checked against the bounds. 286 """ 287 super(Number,self).__init__(default=default,**params) 288 289 self.bounds = bounds 290 self._softbounds = softbounds 291 self.allow_None = (default is None or allow_None) 292 if not callable(default): self._check_value(default)
293 294
295 - def __get__(self,obj,objtype):
296 """ 297 Same as the superclass's __get__, but if the value was 298 dynamically generated, check the bounds. 299 """ 300 result = super(Number,self).__get__(obj,objtype) 301 if self._value_is_dynamic(obj,objtype): self._check_value(result) 302 return result
303 304
305 - def __set__(self,obj,val):
306 """ 307 Set to the given value, raising an exception if out of bounds. 308 """ 309 if not callable(val): self._check_value(val) 310 super(Number,self).__set__(obj,val)
311 312
313 - def set_in_bounds(self,obj,val):
314 """ 315 Set to the given value, but cropped to be within the legal bounds. 316 All objects are accepted, and no exceptions will be raised. See 317 crop_to_bounds for details on how cropping is done. 318 """ 319 if not callable(val): 320 bounded_val = self.crop_to_bounds(val) 321 else: 322 bounded_val = val 323 324 super(Number,self).__set__(obj,bounded_val)
325 326
327 - def crop_to_bounds(self,val):
328 """ 329 Return the given value cropped to be within the hard bounds 330 for this parameter. 331 332 If a numeric value is passed in, check it is within the hard 333 bounds. If it is larger than the high bound, return the high 334 bound. If it's smaller, return the low bound. In either case, the 335 returned value could be None. If a non-numeric value is passed 336 in, set to be the default value (which could be None). In no 337 case is an exception raised; all values are accepted. 338 """ 339 # Currently, values outside the bounds are silently cropped to 340 # be inside the bounds; it may be appropriate to add a warning 341 # in such cases. 342 if (is_number(val)): 343 if self.bounds==None: 344 return val 345 vmin, vmax = self.bounds 346 if vmin != None: 347 if val < vmin: 348 return vmin 349 350 if vmax != None: 351 if val > vmax: 352 return vmax 353 354 elif self.allow_None and val==None: 355 return val 356 357 else: 358 # non-numeric value sent in: reverts to default value 359 return self.default 360 361 return val
362 363
364 - def _check_value(self,val):
365 """ 366 Checks that the value is numeric and that it is within the hard 367 bounds; if not, an exception is raised. 368 """ 369 if self.allow_None and val==None: 370 return 371 372 if not (is_number(val)): 373 raise ValueError("Parameter '%s' only takes numeric values"%(self._attrib_name)) 374 if self.bounds!=None: 375 vmin,vmax = self.bounds 376 if vmin != None and vmax != None: 377 if not (vmin <= val <= vmax): 378 raise ValueError("Parameter '%s' must be between %s and %s (inclusive)"%(self._attrib_name,vmin,vmax)) 379 elif vmin != None: 380 if not vmin <= val: 381 raise ValueError("Parameter '%s' must be at least %s"%(self._attrib_name,vmin)) 382 elif vmax != None: 383 if not val <=vmax: 384 raise ValueError("Parameter '%s' must be at most %s"%(self._attrib_name,vmax))
385 386
387 - def get_soft_bounds(self):
388 """ 389 For each soft bound (upper and lower), if there is a defined bound (not equal to None) 390 then it is returned, otherwise it defaults to the hard bound. The hard bound could still be None. 391 """ 392 if self.bounds==None: 393 hl,hu=(None,None) 394 else: 395 hl,hu=self.bounds 396 397 if self._softbounds==None: 398 sl,su=(None,None) 399 else: 400 sl,su=self._softbounds 401 402 403 if (sl==None): l = hl 404 else: l = sl 405 406 if (su==None): u = hu 407 else: u = su 408 409 return (l,u)
410 411 412
413 -class Integer(Number):
414
415 - def _check_value(self,val):
416 if not isinstance(val,int): 417 raise ValueError("Parameter '%s' must be an integer."%self._attrib_name) 418 super(Integer,self)._check_value(val)
419 420
421 -class Magnitude(Number):
422
423 - def __init__(self,default=1.0,softbounds=None,**params):
424 Number.__init__(self,default=default,bounds=(0.0,1.0),softbounds=softbounds,**params)
425 426 427 # JAB: Should this and other Parameters below be a Dynamic instead?
428 -class Boolean(Parameter):
429 __slots__ = ['bounds','allow_None'] 430 431 # CB: what does bounds=(0,1) mean/do for this Parameter?
432 - def __init__(self,default=False,bounds=(0,1),allow_None=False,**params):
433 self.bounds = bounds 434 self.allow_None = (default is None or allow_None) 435 Parameter.__init__(self,default=default,**params)
436
437 - def __set__(self,obj,val):
438 if self.allow_None: 439 if not isinstance(val,bool) and val is not None: 440 raise ValueError("Boolean '%s' only takes a Boolean value or None." 441 %self._attrib_name) 442 443 if val is not True and val is not False and val is not None: 444 raise ValueError("Boolean '%s' must be True, False, or None."%self._attrib_name) 445 else: 446 if not isinstance(val,bool): 447 raise ValueError("Boolean '%s' only takes a Boolean value."%self._attrib_name) 448 449 if val is not True and val is not False: 450 raise ValueError("Boolean '%s' must be True or False."%self._attrib_name) 451 452 super(Boolean,self).__set__(obj,val)
453 454
455 -class String(Parameter):
456
457 - def __init__(self,default="",**params):
458 """Initialize a string parameter.""" 459 Parameter.__init__(self,default=default,**params)
460
461 - def __set__(self,obj,val):
462 if not isinstance(val,str): 463 raise ValueError("String '%s' only takes a string value."%self._attrib_name) 464 465 super(String,self).__set__(obj,val)
466 467
468 -class NumericTuple(Parameter):
469 __slots__ = ['length'] 470
471 - def __init__(self,default=(0,0),length=None,**params):
472 """ 473 Initialize a numeric tuple parameter with a fixed length. 474 The length is determined by the initial default value, and 475 is not allowed to change after instantiation. 476 """ 477 if length is None: 478 self.length = len(default) 479 else: 480 self.length = length 481 482 self._check(default) 483 Parameter.__init__(self,default=default,**params)
484
485 - def _check(self,val):
486 if not isinstance(val,tuple): 487 raise ValueError("NumericTuple '%s' only takes a tuple value."%self._attrib_name) 488 489 if not len(val)==self.length: 490 raise ValueError("%s: tuple is not of the correct length (%d instead of %d)." % 491 (self._attrib_name,len(val),self.length)) 492 for n in val: 493 if not is_number(n): 494 raise ValueError("%s: tuple element is not numeric: %s." % (self._attrib_name,str(n)))
495
496 - def __set__(self,obj,val):
497 self._check(val) 498 super(NumericTuple,self).__set__(obj,val)
499 500
501 -class XYCoordinates(NumericTuple):
502
503 - def __init__(self,default=(0.0,0.0),**params):
505 506
507 -class Callable(Parameter):
508 """ 509 Parameter holding a value that is a callable object, such as a function. 510 511 A keyword argument instantiate=True should be provided when a 512 function object is used that might have state. On the other hand, 513 regular standalone functions cannot be deepcopied as of Python 514 2.4, so instantiate must be False for those values. 515 """
516 - def __init__(self,default=None,**params):
518
519 - def __set__(self,obj,val):
520 if not callable(val): 521 raise ValueError("Callable '%s' only takes a callable object."%self._attrib_name) 522 super(Callable,self).__set__(obj,wrap_callable(val))
523 524
525 -def wrap_callable(c):
526 """ 527 Wrap a callable object in an InstanceMethodWrapper, if necessary. 528 529 If c is an instancemethod, then wrap it and return the wrapper, 530 otherwise return c. 531 """ 532 if isinstance(c,types.MethodType): 533 return InstanceMethodWrapper(c) 534 else: 535 return c
536 537 538 # CEBALERT: this should be a method of ClassSelector.
539 -def concrete_descendents(parentclass):
540 """ 541 Return a dictionary containing all subclasses of the specified 542 parentclass, including the parentclass. Only classes that are 543 defined in scripts that have been run or modules that have been 544 imported are included, so the caller will usually first do ``from 545 package import *``. 546 547 If the class has an attribute ``abstract``, and it is True, the 548 class will not be included. 549 """ 550 return dict([(c.__name__,c) for c in descendents(parentclass) 551 if not c.abstract])
552 553
554 -class Composite(Parameter):
555 """ 556 A parameter that is in fact a composite of a set of other 557 parameters or attributes of the class. The constructor argumentt 558 'attribs' takes a list of attribute names. Getting the parameter 559 returns a list of the values of the constituents of the composite, 560 in the order specified. Likewise, setting the parameter takes a 561 sequence of values and sets the value of the constituent 562 attributes sets all the constituents 563 """ 564 __slots__=['attribs','objtype'] 565
566 - def __init__(self,attribs=[],**kw):
567 super(Composite,self).__init__(default=None,**kw) 568 self.attribs = attribs
569
570 - def __get__(self,obj,objtype):
571 """ 572 Return the values of all the attribs, as a list. 573 """ 574 if not obj: 575 return [getattr(objtype,a) for a in self.attribs] 576 else: 577 return [getattr(obj,a) for a in self.attribs]
578
579 - def __set__(self,obj,val):
580 """ 581 Set the values of all the attribs. 582 """ 583 assert len(val) == len(self.attribs),"Compound parameter '%s' got the wrong number of values (needed %d, but got %d)." % (self._attrib_name,len(self.attribs),len(val)) 584 585 if not obj: 586 for a,v in zip(self.attribs,val): 587 setattr(self.objtype,a,v) 588 else: 589 for a,v in zip(self.attribs,val): 590 setattr(obj,a,v)
591 592
593 -class Selector(Parameter):
594 """ 595 Parameter whose value is set to some form of one of the 596 possibilities in its range. 597 598 Subclasses must implement get_range(). 599 """ 600 __abstract = True 601
602 - def get_range(self):
603 raise NotImplementedError("get_range() must be implemented in subclasses.")
604 605
606 -class ObjectSelector(Selector):
607 """ 608 Parameter whose value is set to an object from its list of possible objects. 609 """ 610 __slots__ = ['objects','allow_None'] 611
612 - def __init__(self,default=None,objects=[],instantiate=True,allow_None=False,**params):
617 618 # CBNOTE: if the list of objects is changed, the current value for 619 # this parameter in existing POs could be out of the new range. 620
621 - def _check_value(self,val,obj=None):
622 """ 623 val must be None or one of the objects in self.objects. 624 """ 625 if not (val in self.objects) and not (val is None and self.allow_None): 626 raise ValueError("%s not in Parameter %s's list of possible objects" \ 627 %(val,self._attrib_name))
628 629 # CBNOTE: I think it's not helpful to do a type check for the value of 630 # an ObjectSelector. If we did such type checking, any user 631 # of this Parameter would have to be sure to update the list of possible 632 # objects before setting the Parameter's value. As it is, only users who care about the 633 # correct list of objects being displayed need to update the list. 634 ## def __set__(self,obj,val): 635 ## self._check_value(val,obj) 636 ## super(ObjectSelector,self).__set__(obj,val) 637 638 639 # CebAlert; move some bits into superclass (same for clsselector)?
640 - def get_range(self):
641 """ 642 Return the possible objects to which this parameter could be set. 643 644 (Returns the dictionary {object.name:object}.) 645 """ 646 d=dict([(obj.name,obj) for obj in self.objects]) 647 if self.allow_None: 648 d['None']=None 649 return d
650 651
652 -class ClassSelector(Selector):
653 """ 654 Parameter whose value is an instance of the specified class. 655 """ 656 # CEBALERT: allow_None already a slot from superclass? 657 __slots__ = ['class_','allow_None'] 658
659 - def __init__(self,class_,default=None,instantiate=True,allow_None=False,**params):
664 665
666 - def _check_value(self,val,obj=None):
667 """val must be None or an instance of self.class_""" 668 if not (isinstance(val,self.class_)) and not (val is None and self.allow_None): 669 raise ValueError( 670 "Parameter '%s' value must be an instance of %s, not '%s'" % 671 (self._attrib_name, self.class_.__name__, val))
672
673 - def __set__(self,obj,val):
674 self._check_value(val,obj) 675 super(ClassSelector,self).__set__(obj,val)
676 677
678 - def get_range(self):
679 """ 680 Return the possible types for this parameter's value. 681 682 (I.e. return {name: <class>} for all classes that are 683 concrete_descendents() of self.class_.) 684 685 Only classes from modules that have been imported are added 686 (see concrete_descendents()). 687 """ 688 classes = concrete_descendents(self.class_) 689 d=dict([(name,class_) for name,class_ in classes.items()]) 690 if self.allow_None: 691 d['None']=None 692 return d
693 694
695 -class List(Parameter):
696 """ 697 Parameter whose value is a list of objects, usually of a specified type. 698 699 The bounds allow a minimum and/or maximum length of 700 list to be enforced. If the class is non-None, all 701 items in the list are checked to be of that type. 702 """ 703 __slots__ = ['class_','bounds'] 704
705 - def __init__(self,default=[],class_=None,instantiate=True, 706 bounds=(0,None),**params):
712 713 # Could add range() method from ClassSelector, to allow 714 # list to be populated in the GUI 715
716 - def __set__(self,obj,val):
717 """Set to the given value, raising an exception if out of bounds.""" 718 self._check_bounds(val) 719 super(List,self).__set__(obj,val)
720
721 - def _check_bounds(self,val):
722 """ 723 Checks that the list is of the right length and has the right contents. 724 Otherwise, an exception is raised. 725 """ 726 if not (isinstance(val,list)): 727 raise ValueError("List '%s' must be a list."%(self._attrib_name)) 728 729 if self.bounds!=None: 730 min_length,max_length = self.bounds 731 l=len(val) 732 if min_length != None and max_length != None: 733 if not (min_length <= l <= max_length): 734 raise ValueError("%s: list length must be between %s and %s (inclusive)"%(self._attrib_name,min_length,max_length)) 735 elif min_length != None: 736 if not min_length <= l: 737 raise ValueError("%s: list length must be at least %s."%(self._attrib_name,min_length)) 738 elif max_length != None: 739 if not l <= max_length: 740 raise ValueError("%s: list length must be at most %s."%(self._attrib_name,max_length)) 741 742 self._check_type(val)
743
744 - def _check_type(self,val):
745 if self.class_!=None: 746 for v in val: 747 assert isinstance(v,self.class_),repr(v)+" is not an instance of " + repr(self.class_) + "."
748 749 750
751 -class HookList(List):
752 """ 753 Parameter whose value is a list of callable objects. 754 755 This type of List Parameter is typically used to provide a place 756 for users to register a set of commands to be called at a 757 specified place in some sequence of processing steps. 758 """ 759 __slots__ = ['class_','bounds'] 760
761 - def _check_type(self,val):
762 for v in val: 763 assert callable(v),repr(v)+" is not callable."
764 765 766
767 -class Dict(ClassSelector):
768 """ 769 Parameter whose value is a dictionary. 770 """
771 - def __init__(self,**params):
772 super(Dict,self).__init__(dict,**params)
773 774 775
776 -class InstanceMethodWrapper(object):
777 """ 778 Wrapper for pickling instance methods. 779 780 The constructor takes an instance method (e.g. for an object 781 'sim', method sim.time) as its only argument. The wrapper 782 instance is callable, picklable, etc. 783 """ 784 # CEBALERT: Both repr and name disguise that this is an 785 # InstanceMethodWrapper. 786
787 - def __repr__(self):
788 return repr(self.im.im_func)
789 790 # Hope __name__ doesn't get set...
791 - def _fname(self):
792 return self.im.im_func.func_name
793 __name__ = property(_fname) 794
795 - def __init__(self,im):
796 self.im = im
797
798 - def __getstate__(self):
799 return (self.im.im_self, 800 self.im.im_func.func_name)
801
802 - def __setstate__(self,state):
803 obj,func_name = state 804 self.im = getattr(obj,func_name)
805
806 - def __call__(self,*args,**kw):
807 return self.im(*args,**kw)
808