How safe is modifying locals()?

Paul Paterson paulpaterson at users.sourceforge.net
Sun Jul 27 00:51:11 EDT 2003


Bengt Richter wrote:

> On Sat, 26 Jul 2003 15:20:24 GMT, Paul Paterson <paulpaterson at users.sourceforge.net> wrote:
> 
> 
>>Stefan Schwarzer wrote:
>>
>>
>>>Hi Paul
>>>
>>>Paul Paterson wrote:
>>>
>>>
>>>>This is a very interesting (and Pythonic) approach, thanks for 
>>>>suggesting it! This is certainly what I would try to do if I were 
>>>>writing the code from scratch. It may be possible to construct a 
>>>>"namespace" type object which gets passed to the function.
>>>
>>>
>>>I think the most common way to make a namespace in Python is
>>
>><snip nice example of namespace class>
>>
>>Thanks, the bare namespace class is essentially what I am going to try.
>>
>>
>>>Of course, if you have a better name for your container, that's even
>>>better. Think of Ian's Point example. :-)
>>
>>I think you may have missed my original post; I am writing a generic VB 
>>to Python converter so generating good names based on code intent would 
>>be pretty tough! Perhaps in v2.0 ;)
>>
>>My current thinking is (for the orignal example of changing one 
>>variable, 'y' but not another, 'x'),
>>
>>class Namespace: pas
>>
>># ByVal arguments passed directly, ByRef via a namespace
>>def change(x, byrefs):
>>    x = x + 1
>>    byrefs.y = byrefs.y + 1
>>
>>ns = Namespace() # The local namespace
>>ns.x = 0
>>ns.y = 0
>>change(ns.x, ns)
>># Now ns.x = 0, ns.y = 1
>>
> 
> This looks readable, to me ;-)
> 
> But it is not pointer semantics, since you have to know the name for y inside change.

For my application this is always the case ... but sometimes treating 
the problem as harder than it really is leads to a solution that is 
better than you would get otherwise!

> It will be nice and fast if you can use the above, but if not, in some cases, class NSP
> below will let you use it just like Namespace above, though with pointer features if
> you need them.
> 
> I just wanted to throw in here that the namespace approach will also let you
> use properties (there's a handy builtin to create them from accessor methods,
> or you can instantiate them from your own descriptor classes) as your
> byrefs "variables" if you like.
> 
> You just have to mane the NameSpace class inherit from object, to make it
> a new-style class. Then you can add properties either right in the class definition
> or later by setting class attributes , e.g.,

<snip interesting development of the property approach>

> A namespace for variables also gives you the potential to define methods for the name space
> itself. Here is an example with a name space that has an internal pointer class and lets
> you create "pointers" to the "variables" in the namespace, using ns[vname] syntax, which
> goes to the __getitem__ method of the class to make a "pointer" (__slots__ hopefully just
> makes pointer instances better optimized):
> 
>  >>> class NSP(object):
>  ...     """NSP defines name space with "pointer" creation via p = ns[vname]"""
>  ...     class Ptr(object):
>  ...         """Ptr instance holds ns ref and vname for access to ns.vname"""
>  ...         __slots__ = 'ns', 'vname'
>  ...         def __init__(self, ns, vname): self.ns=ns; self.vname=vname
>  ...         def __getitem__(self, i):
>  ...             """typical: x=p[:]"""
>  ...             return getattr(self.ns, self.vname)
>  ...         def __setitem__(self, i, v):
>  ...             """Typical: p[:] = x"""
>  ...             setattr(self.ns, self.vname, v)
>  ...         def __delitem__(self, i):
>  ...             """Typical: del p[:] # deletes what's pointed to"""
>  ...             delattr(self.ns, self.vname)
>  ...     def __getitem__(self, name):
>  ...         """Make "pointer." Typical: p2x = ns['x']; p2x[:] => ns.x"""
>  ...         return self.Ptr(self, name)
>  ...
>  >>> ns = NSP()
>  >>> ns.x = 123
>  >>> ns.y = 456
>  >>> def change(x, py):
>  ...     x = 'new x?'
>  ...     py[:] = 'new via py[:]'
>  ...
>  >>> ns.x
>  123
>  >>> ns.y
>  456
>  >>> change(ns.x, ns['y']) # on the fly &y
>  >>> ns.x
>  123
>  >>> ns.y
>  'new via py[:]'
>  >>>

To my eye, the [:] or [0] spelling of this makes the code look more 
complex than necessary, but I think you are on to something because if 
you spell it,

def change(x, y):
    x = 'new x'
    y.update('new y')

with the relevant changes to the Ptr class then it could certainly grow 
on me. The things I like are,

- no new variable names in the 'change' function so it looks similar to 
the original code
- the mechanism for propogating changes to the caller scope is explicit
- 'y' can be passed on to another function if needed and things are 
still clear

eg,

def change(x, y):
     x = 'new x'
     change2(y)

def change2(y):
     y.update('deep change in y')

To do this using the original namespace approach gets a little tricky 
because you have to merge the namespaces as you go. The pointer idea 
flattens that structure.

<snip stuff about deleting a pointer>


> I'm not necessarily recommending any of the above, but I thought it might give you some ideas..
> There is definitely a performance hit in px[:] (or better, px[0]) vs byref.x to consider.
> 
> Anyway, I thought properties and descriptors might also come in handy somewhere in your quest ;-)

This is certainly food for thought. My main criteria is this: I don't 
want to translate clearly written code in an ugly language (VB) into 
ugly code in a clear language (Python). If this happens I might as well 
pack up and go home!

So I will trade speed for clarity almost always because someone can 
always optimize clear code later.

Thanks for these thoughts and the time it took to post them, they really 
made me think! (I mean that in a good way, of course ;) )

Paul





More information about the Python-list mailing list