scoping/module life puzzle
Bengt Richter
bokr at oz.net
Mon Dec 2 17:13:37 EST 2002
On Fri, 29 Nov 2002 11:23:31 +0000, Robin Becker <robin at jessikat.fsnet.co.uk> wrote:
>I would be grateful if someone can explain what's going on here. First
>off why do I need to keep a reference to the module (self._m=m) If I
>don't then it seems as though the module is dismembered (join-->None)
>even though the module object has an extant reference the run method.
>
>Secondly even if I keep the module referenced why does A().run()(L)
>crash when a=A();a.run()(L) doesn't?
>
Interesting question. On the face of it, it looks like a bug. I.e., as though
a temp object is being created without incrementing and then decrementing a ref
count for something it's using, maybe mistakenly assuming a reference somewhere
can just be borrowed?
But the other question is, why do you need a module? If you just want
dynamically to create and cache a function called run, why not just do it? E.g.,
######################
code='''from string import join
def run(L):
print 'in run before join'
X = join(L,' ')
print 'in run after join',
return X
'''
def mkrunfun(code, cache={}):
try:
return cache[code]
except KeyError:
d = {}; exec code in d
return cache.setdefault(code, d.get('run'))
L=['a','b','c']
frun = mkrunfun(code)
print 1,frun(L)
print 2,mkrunfun(code)(L)
######################
outputs:
1 in run before join
in run after join a b c
2 in run before join
in run after join a b c
Or am I oversimplifying? Perhaps you need to access various names like "run"
as attributes of a returned object, in case the code string has multiple things?
Not very tested, but maybe something like:
######################
code='''from string import join
def run(L):
print 'in run before join'
X = join(L,' ')
print 'in run after join',
return X
'''
class NS: pass
def mkrunobj(code, cache={}):
try:
return cache[code]
except KeyError:
ns = NS()
exec code in ns.__dict__
return cache.setdefault(code, ns)
L=['a','b','c']
orun = mkrunobj(code)
print 1,orun.run(L)
print 2,mkrunobj(code).run(L)
print 3,mkrunobj(code,{}).run(L)
######################
outputs:
1 in run before join
in run after join a b c
2 in run before join
in run after join a b c
3 in run before join
in run after join a b c
I guess you could also write a metaclass to turn functions into static methods and
have a dynamically created class play about the same role as an NS instance above.
>######################
>code='''from string import join
^^^^^^^^^^^^^^^^^^^^^^^-- if you move this down into run ...
>def run(L):
from string import join # ... to here, it will work, but do you really need a module??
> print 'in run before join',
> X = join(L,' ')
> print 'in run after join',
> return X
>'''
>class A:
> def run(self):
> m = getattr(self,'_m',None)
> if not m:
> from imp import new_module
> m = new_module('myrunner')
> exec code in m.__dict__
> self._m = m #we must have this or nothing works
> return m.run
>
> def do_run(self,L):
> return self.run()(L)
>L=['a','b','c']
>a=A()
>print 0,a.do_run(L)
>print 1,a.run()(L)
>print 2,A().do_run(L)
>print 3,A().run()(L)
>######################
>output with self._m=m ie keep a ref to the module
>
>C:\>\tmp\tmload.py
>0 in run before join in run after join a b c
>1 in run before join in run after join a b c
>2 in run before join in run after join a b c
>3 in run before join
>Traceback (most recent call last):
> File "C:\tmp\tmload.py", line 25, in ?
> print 3,A().run()(L)
> File "<string>", line 4, in run
>TypeError: 'NoneType' object is not callable
>######################
>If I comment self._m = m then nothing works
>
>C:\>\tmp\tmload.py
>0 in run before join
>Traceback (most recent call last):
> File "C:\tmp\tmload.py", line 22, in ?
> print 0,a.do_run(L)
> File "C:\tmp\tmload.py", line 19, in do_run
> return self.run()(L)
> File "<string>", line 4, in run
>TypeError: 'NoneType' object is not callable
>
>--
>Robin Becker
Regards,
Bengt Richter
More information about the Python-list
mailing list