That horrible regexp idiom

Duncan Booth duncan.booth at invalid.invalid
Thu Feb 10 09:20:57 EST 2005


Nick Coghlan wrote:

> I knew if/elif was a much better argument in favour of embedded
> assignment than while loops are.
> 

I know I'm going to regret posting this, but here is an alternative, very 
hackish way to do all those things people keep asking for, like setting 
variables in outer scopes or doing lots of nested ifs for regex matching or 
simulating passing a local variable by reference.

My excuse for the following code is that was unwell and I think I was 
slightly feverish when I wrote it:

----- hack.py -----------
import new

def mksetter(baa):
    '''Cruel hack to set scoped variables.
    baa should be a lambda which accesses the variable you wish to set.
    It must be a local or outer scope variable, not a global
    '''
    closure = baa.func_closure
    name = baa.func_code.co_names[0]
    if not isinstance(closure, tuple) or len(closure) != 1:
        raise TypeError('''mksetter() argument must be a lambda accessing a 
local or scoped variable''')

    def f(v):
        a = v
        return v
        lambda: a

    c = f.func_code
    newcode = new.code(
        c.co_argcount, 1, c.co_stacksize, c.co_flags, c.co_code,
        c.co_consts, c.co_names, c.co_varnames, c.co_filename, 'set_'+name,
        c.co_firstlineno, c.co_lnotab, (name,), (),
        )
    return new.function(newcode, f.func_globals,
        'set_'+name, None, closure)

if __name__=='__main__':
    def set_a_local():
        a = 42
        print "a=",a
        set_a = mksetter(lambda: a)
        set_a(24)
        print set_a
        print "a=",a

    set_a_local()

    def set_scoped():
        count = 0
        set_count = mksetter(lambda: count)
        def inc():
            set_count(count+1)
        for i in range(10):
            print "count=",count
            inc()
        print "Final value",count

    set_scoped()

    import re
    def silly_regex():
        name = re.compile('[a-zA-Z_][a-zA-Z0-9_]*')
        number = re.compile('[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?')

        m = None
        set_m = mksetter(lambda: m)

        for s in 'abc', '2.5e-3', '!':
            if set_m(name.match(s)):
                print "name", m.group(0)
            elif set_m(number.match(s)):
                print "number", m.group(0)
            else:
                print "no match", m, s

    silly_regex()

----- end of hack.py ----

Sorry.

If anyone actually feels tempted to use this: I take no responsibility for 
the results. I can't particularly see why future versions of Python would 
change to stop it working, but they could, and of course it is definitely a 
'C-Python only' trick.





More information about the Python-list mailing list