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