> The only cases I see the first school of thought is when the resource > in question is "scarce" in some way.
By "resource" I meant anything with some sort of acquire/release semantics. There may be plenty of threading.Locks available, but it's still important that a given Lock is released when not needed. For example, most OS's place a > > class withFile(blockScopedResource): > > init, cleanup = open, 'close' > Well, I'd say that using a string for cleanup and a function for init > is unpythonic. I could have specified cleanup as lambda f:f.close(), but as I thought it might be quite common to call a method on the resourse for cleanup, if a string is specified a method of that name is used instead. > The question is whether having to turn your scope into a > function to do this is more trouble than it's worth. Needing one slightly contrived-looking line (the def) vs a try-finally block with explicit cleanup code? I know which I'd prefer, but for all I know I could in a minority of 1 here. > I'd certainly be interested in seeing the implementation. And so you shall... I start with the base class. It does all the work, everything else is just tweaks for convenience. Normally, then, you wouldn't need to bother with all the __init__ params. class blockScopedResource(object): def __init__(self, init, cleanup, initArgs, initKwargs, cleanupArgs, cleanupKwargs, passResource, resourceIsFirstArg): self.init = init # function to get resource self.cleanup = cleanup # function to release resource self.initArgs, self.initKwargs = initArgs, initKwargs self.cleanupArgs, self.cleanupKwargs = cleanupArgs, cleanupKwargs self.passResource = passResource # whether resource is passed into block self.resourceIsFirstArg = resourceIsFirstArg # whether resource is arg to init, # rather than returned from it def __call__(self, block): resource = self.init(*self.initArgs, **self.initKwargs) if self.resourceIsFirstArg: resource = self.initArgs[0] try: if self.passResource: block(resource) else: block() finally: self.cleanup(resource, *self.cleanupArgs, **self.cleanupKwargs) But this still won't do conveniently for files and locks, which are my motivating examples. The simpleResource class constructor gets its setup from attributes on the type of the object being created, with sensible defaults being set on simpleResource itself. As stated above, if a string is supplied as init or cleanup, it is treated as a method name and that method is used instead. def stringToMethod(f): # Getting the attribute from the class may have wrapped it into # an unbound method; in this case, unwrap it if isinstance(f, types.MethodType) and f.im_self is None: f = f.im_func if not isinstance(f, basestring): return f def helper(resource, *args, **kwargs): return getattr(resource, str(f))(*args, **kwargs) return helper class simpleResource(blockScopedResource): def __init__(self, *initArgs, **initKwargs): # get attributes off type t = type(self) blockScopedResource.__init__(self, stringToMethod(t.init), stringToMethod(t.cleanup), initArgs, initKwargs, t.cleanupArgs, t.cleanupKwargs, t.passResource, t.resourceIsFirstArg) # defaults supplied here cleanupArgs, cleanupKwargs = (), {} passResource = True resourceIsFirstArg = False Then useful implementations can be written by: class withFile(simpleResource): init, cleanup = open, 'close' class withLock(simpleResource): init, cleanup = 'acquire', 'release' passResource = False resourceIsFirstArg = True And new ones can be created with a similar amount of effort. Of course, one-liners can be done without using the decorator syntax: withLock(aLock)(lambda:doSomething(withAnArg)) Gotcha: If you stack multiple resource-decorator it won't do what you want: # !!! DOESN'T WORK !!! @withLock(aLock) @withLock(anotherLock) def do(): # ... Either nest them explicitly (causing your code you drift ever further to the right): @withLock(aLock) def do(): @withLock(anotherLock) def do(): # ... Or come up with a multiple-resource handler, which shouldn't be too hard: @withResources(withLock(aLock), withLock(anotherLock), withFile('/dev/null')) But I'll get round to that another day. -- http://mail.python.org/mailman/listinfo/python-list