From 64a781b7b5ae98b2a410799497586cbb085f7d99 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 21 Aug 2010 11:33:46 -0400 Subject: [PATCH] Serialize hooke.experiment.Experiment instances in Curves. Also use singleton instances of the classes rather than the classes themselves. This will allow usage of isinstance() rather than .__mro__ to determine experiment type. --- hooke/curve.py | 15 +++++++++++ hooke/driver/hemingway.py | 2 +- hooke/driver/jpk.py | 2 +- hooke/driver/mfp3d.py | 2 +- hooke/driver/picoforce.py | 2 +- hooke/driver/tutorial.py | 2 +- hooke/driver/wtk.py | 2 +- hooke/experiment.py | 5 +++- hooke/util/singleton.py | 57 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 hooke/util/singleton.py diff --git a/hooke/curve.py b/hooke/curve.py index 629e0a8..af572f3 100644 --- a/hooke/curve.py +++ b/hooke/curve.py @@ -26,6 +26,7 @@ import os.path import numpy from .command_stack import CommandStack +from . import experiment class NotRecognized (ValueError): @@ -195,6 +196,12 @@ class Curve (object): dc = state['command_stack'] if hasattr(dc, '__getstate__'): state['command_stack'] = dc.__getstate__() + if self.info.get('experiment', None) != None: + e = self.info['experiment'] + assert isinstance(e, experiment.Experiment) + # HACK? require Experiment classes to be defined in the + # experiment module. + self.info['experiment'] = e.__class__.__name__ return state def __setstate__(self, state): @@ -203,6 +210,14 @@ class Curve (object): if key == 'path': self.set_path(value) continue + elif key == 'info': + if 'experiment' not in value: + value['experiment'] = None + else: + # HACK? require Experiment classes to be defined in the + # experiment module. + cls = getattr(experiment, value['experiment']) + value['experiment'] = cls() elif key == 'command_stack': v = CommandStack() v.__setstate__(value) diff --git a/hooke/driver/hemingway.py b/hooke/driver/hemingway.py index e65b613..af351f7 100644 --- a/hooke/driver/hemingway.py +++ b/hooke/driver/hemingway.py @@ -74,5 +74,5 @@ class HemingwayDriver (Driver): ret[:,0] = numpy.arange(0, 1e-3*data.shape[0], 1e-3, dtype=ret.dtype) file_info['filetype'] = self.name - file_info['experiment'] = experiment.ForceClamp + file_info['experiment'] = experiment.ForceClamp() return ([ret,], file_info) diff --git a/hooke/driver/jpk.py b/hooke/driver/jpk.py index 39717ef..6c95fe9 100644 --- a/hooke/driver/jpk.py +++ b/hooke/driver/jpk.py @@ -154,7 +154,7 @@ class JPKDriver (Driver): # and velocity clamp segments in a single experiment), but I # have no idea what sort of analysis such experiments would # require ;). - info['experiment'] = experiment.VelocityClamp + info['experiment'] = experiment.VelocityClamp() force_unit = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['scaling']['unit']['unit'] assert force_unit == 'N', force_unit force_base = chan_info['channel']['vDeflection']['conversion-set']['conversion']['force']['base-calibration-slot'] diff --git a/hooke/driver/mfp3d.py b/hooke/driver/mfp3d.py index 6d2cdc5..3347e3d 100644 --- a/hooke/driver/mfp3d.py +++ b/hooke/driver/mfp3d.py @@ -73,7 +73,7 @@ class MFP3DDriver (Driver): data,bin_info,wave_info = loadibw(path) blocks,info = self._translate_ibw(data, bin_info, wave_info) info['filetype'] = self.name - info['experiment'] = experiment.VelocityClamp + info['experiment'] = experiment.VelocityClamp() return (blocks, info) def _translate_ibw(self, data, bin_info, wave_info): diff --git a/hooke/driver/picoforce.py b/hooke/driver/picoforce.py index 1f1fefb..ca84792 100644 --- a/hooke/driver/picoforce.py +++ b/hooke/driver/picoforce.py @@ -55,7 +55,7 @@ class PicoForceDriver (Driver): self._check_version(info) data = self._read_data_path(path, info) info['filetype'] = self.name - info['experiment'] = experiment.VelocityClamp + info['experiment'] = experiment.VelocityClamp() return (data, info) def _read_header_path(self, path): diff --git a/hooke/driver/tutorial.py b/hooke/driver/tutorial.py index 7fe5298..6856f8d 100644 --- a/hooke/driver/tutorial.py +++ b/hooke/driver/tutorial.py @@ -136,5 +136,5 @@ class TutorialDriver (Driver): f.close() # remember to close the file data = curve.Data() - info = {'filetype':'tutorial', 'experiment':experiment.Experiment} + info = {'filetype':'tutorial', 'experiment':experiment.Experiment()} return (data, info) diff --git a/hooke/driver/wtk.py b/hooke/driver/wtk.py index b39bdd9..4124701 100644 --- a/hooke/driver/wtk.py +++ b/hooke/driver/wtk.py @@ -98,7 +98,7 @@ class WTKDriver (Driver): unlabeled_approach_data, params, 'approach') retract = self._scale_block( unlabeled_retract_data, params, 'retract') - info = {'filetype':self.name, 'experiment':experiment.VelocityClamp} + info = {'filetype':self.name, 'experiment':experiment.VelocityClamp()} return ([approach, retract], info) def _paths(self, path): diff --git a/hooke/experiment.py b/hooke/experiment.py index 81decf9..646b415 100644 --- a/hooke/experiment.py +++ b/hooke/experiment.py @@ -22,7 +22,10 @@ This allows :class:`hooke.plugin.Plugin`\s to specify the types of experiments they can handle. """ -class Experiment (object): +from .util.singleton import Singleton + + +class Experiment (Singleton): """Base class for experiment classification. """ pass diff --git a/hooke/util/singleton.py b/hooke/util/singleton.py new file mode 100644 index 0000000..26a657e --- /dev/null +++ b/hooke/util/singleton.py @@ -0,0 +1,57 @@ +# Copyright + +"""Define the :class:`Singleton` class. + +>>> class A (Singleton): +... def init(self): +... print 'initializing instance of %s at (%s)' % ( +... self.__class__.__name__, id(self)) + +>>> A_instances = [A() for i in range(3)] # doctest: +ELLIPSIS +initializing instance of A at (...) +>>> for i in A_instances[1:]: +... print id(i) == id(A_instances[0]) +True +True + +Singletons can also be subclassed. + +>>> class B (A): +... pass +>>> B_instances = [B() for i in range(3)] # doctest: +ELLIPSIS +initializing instance of B at (...) +>>> for i in B_instances[1:]: +... print id(i) == id(B_instances[0]) +True +True +>>> id(A_instances[0]) == id(B_instances[0]) +False +""" + +class Singleton (object): + """A singleton class. + + To create a singleton class, you subclass from Singleton; each + subclass will have a single instance, no matter how many times its + constructor is called. To further initialize the subclass + instance, subclasses should override 'init' instead of __init__ - + the __init__ method is called each time the constructor is called. + + Notes + ----- + Original implementation from Guido van Rossum's + `Unifying types and classes in Python 2.2`_. + + .. Unifying types and classes in Python 2.2: + http://www.python.org/download/releases/2.2.3/descrintro/#__new__ + """ + def __new__(cls, *args, **kwds): + it = cls.__dict__.get('__it__') + if it is not None: + return it + cls.__it__ = it = object.__new__(cls) + it.init(*args, **kwds) + return it + + def init(self, *args, **kwds): + pass -- 2.26.2