pythonic equivalent of upvar?

Bengt Richter bokr at oz.net
Tue Dec 20 13:02:40 EST 2005


On Tue, 20 Dec 2005 16:10:30 +0200, mackay at aims.ac.za (David MacKay) wrote:

>Dear Greater Py,
>
><motivation note="reading this bit is optional">
>     I am writing a command-line reader for python.
>
>     I'm trying to write something with the same brevity 
>as perl's one-liner
>
>eval "\$$1=\$2" while @ARGV && $ARGV[0]=~ /^(\w+)=(.*)/ && shift;
>
>and with similar functionality.  I've decided I don't like 
>getopt, because it seems to require me to write pages and pages 
>of elif's as I add new arguments.  It slows me down, whereas the 
>perlism above liberates.
>
>My solution is a twenty-line python chunk equivalent to the perl one-liner. 
>(Twenty lines because, to process a (name, value) pair, I have to find the 
>current type of the variable "name" before setting "name" to righttype("value").
>
>I'm happy with this 20-line chunk and wish to re-use it in many python programs.
></motivation>
>
><question>
> What is the pythonic way to embed a 20-line chunk of code into a function?
>I'd love to define these 20 lines as a module, and reuse the module over and over.
>but this chunk of code needs to have access to 
>the local variables of main, whatever they are called. 
>
> In tcl, IIRC, the command "upvar" allows one function to get access to its 
>parent function's local variables.
>
> Is there a pythonic way to tell a function "you are allowed to see all your 
>parent's variables?"  Or, to tell a chunk of code, "you are just a chunk of 
>code, not really a function"?
></question>
>
You can see them, but you can't rebind them without nasty hacks.
I would suggest just instantiating an object that initializes the way
you want to contain the values you pre-specify, and optionally get
overridden by command line info as you specify. E.g., a quick hack (not
tested beyond what you see ;-)

----< clvalues.py >-----------------------------------------------------
class CLValues(dict):
    """
    A dict whose initialization values are overridden by
    -name value pairs in the overrides list, by default taken from
    command line args sys.argv[1:], but a list may be passed.
    Existing values get overridden by cmd line values converted
    to the same type. New values are assumed str type. Existing
    values may be specified non-overridable by prefixing an underscore,
    which is removed after cmd line args have been processed.
    Since keys are valid names, access is also provided via attribute syntax.
    """
    def __init__(self, overrides=None, *args, **kw):
        dict.__init__(self, *args, **kw)
        if overrides is None: overrides = __import__('sys').argv[1:]
        while overrides:
            name = overrides.pop(0)
            if not name.startswith('-') or not name[1:]:
                raise ValueError, "Names must be non-null and prefixed with '-', unlike %r" % name
            name = name[1:]
            if '_'+name in self or name.startswith('_') and name in self:
                raise ValueError, "%r may not be overridden" % name
            if not overrides: raise ValueError, 'No value following %r'% '-'+name
            value = overrides.pop(0)
            self[name] = type(self.get(name, ''))(value) # default str type if not pre-existing
        for k,v in self.items():
            if k.startswith('_'): # make plain names for non-overridables
                self[k[1:]] = v
                del self[k]
    def __getattr__(self, name): return self[name] # provide key access as attributes

def test():
    clv = CLValues(
       decode=0,
       verbose=0,
       bits=7,
       N = 10000,
       file="blah",
       _securityLevel = 3)
    for name, value in clv.items():
        print '%15s: %r' %(name, value)
    print 'N accessed as clv.N => %r' % clv.N

if __name__ == '__main__':
    test()
------------------------------------------------------------------------

Running it to run the test:

[ 9:54] C:\pywk\ut>py24 clvalues.py -bits 8 -file other\path\and\file
         decode: 0
  securityLevel: 3
        verbose: 0
           file: 'other\\path\\and\\file'
           bits: 8
              N: 10000
N accessed as clv.N => 10000

[ 9:54] C:\pywk\ut>py24 clvalues.py -securityLevel 0
Traceback (most recent call last):
  File "clvalues.py", line 44, in ?
    test()
  File "clvalues.py", line 38, in test
    _securityLevel = 3)
  File "clvalues.py", line 21, in __init__
    raise ValueError, "%r may not be overridden" % name
ValueError: 'securityLevel' may not be overridden

Quoting command line arg to make a single arg with embedded spaces:

[ 9:54] C:\pywk\ut>py24 clvalues.py -added "should be string"
         decode: 0
  securityLevel: 3
          added: 'should be string'
        verbose: 0
           file: 'blah'
           bits: 7
              N: 10000
N accessed as clv.N => 10000


>Thanks very much
>
De nada.

>David
>
>PS -- example below illustrates the chunk of code, in case anyone is interested.
>
<snip code, from which I tried to translate the functionality>

Usage in a program would go something like
   from clvalues import CLValues
   clv = CLValues(
       # ... like test above
   )
   # ...
   # use values spelled clv.name or clv['name'] or clv.get('name') etc
   #

Regards,
Bengt Richter



More information about the Python-list mailing list