[...]
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')
For a general pointer, ISTM you want to be able to dereference it for both getting and setting.
The [:] or [0] syntax gets you that, and you could do it with p() for getting and p(val) for setting,
or p.getval() and p.update(), but all these get clumsy when you want to use the "pointer" on
both sides of an assignment statement, e.g.,
p.update(p.getval()+' added text for target')
where you could write more readably (IMO),
p.value = p.value + 'added text for target'
or
p.v += 'added text for target'
Below is a class PNS that lets you spell as immediately above, using .value
(and .v for concise use) bound to a property that implements the accessing of
what's pointed/referred to. I gave it a somewhat informative __repr__ method also,
so the test prints better. As you will note, this is now separate from any particular
name space. Any object that supports getattr and/or setattr can be used. I also
threw in a permissions parameter to control read/write/delete. See nsother and math
in examples. Note also that a pointer may point to another pointer, allowing cascaded
dereferencing spelled p.v.v etc.
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')
If you use PNS, that will be spelled
def change(x, y):
x = 'new x'
change2(y)
def change2(y):
y.value = 'deep change in y'
See test() code below. I used your original class Namespace: pass as the main namespace.
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.
====< pns.py >==========================================================
class PNS(object):
"""
Pointer to Name Space
PNS instance holds ns ref and vname for access to ns.vname
Read, write, delete access permitted if r,w,d in perms, respectively.
Typical: ptr_to_x = PNS(ns, 'x')
"""
__slots__ = 'ns vname ok_r ok_w ok_d'.split()
class PNSError(Exception):
def __init__(self, ptr, msg):
Exception.__init__(self, '%s for %r' %(msg, ptr))
def __init__(self, ns, vname, perms='rwd'):
self.ns=ns; self.vname=vname
self.ok_r = 'r' in perms
self.ok_w = 'w' in perms
self.ok_d = 'd' in perms
def _getv(self):
"""Typical read access: x = ptr.value (x = ptr.v works too)"""
if self.ok_r: return getattr(self.ns, self.vname)
raise self.PNSError(self, 'Value read prohibited')
def _setv(self, v):
"""Typical write access: ptr.value = 'new x' (ptr.v = 'new x' works too)"""
if self.ok_w: setattr(self.ns, self.vname, v)
else: raise self.PNSError(self, 'Value write prohibited')
def _delv(self):
"""Typical del access: del ptr.value (del ptr.v works too)"""
if self.ok_d: delattr(self.ns, self.vname)
else: raise self.PNSError(self, 'Value deletion prohibited')
value = v = property(_getv, _setv, _delv) # .v for short
def __repr__(self): return '<PNS ptr to %r of %r>'%(self.vname, self.ns)
class Namespace(object): pass
def test():
ns = Namespace()
ns.x = 'x value'
ns.y = 'y value'
print 'Before change:'
print 'ns.x=%r, ns.y=%r' %(ns.x, ns.y)
print 'Making pointer py point to ns.y ...'
py = PNS(ns, 'y') # prefixing pointer names with 'p' is not mandatory, just mnemonic
print 'ptr py=%r' % py
def change(x, y): # prefixing pointer names with 'p' is not mandatory, just mnemonic
x = 'new x'
y.value = 'new y'
print 'Calling change(ns.x, py) ...'
change(ns.x, py)
print 'After change:'
print 'ns.x=%r, ns.y=%r' %(ns.x, ns.y)
def change1(x, y):
x = 'new x'
change2(y)
def change2(y):
y.v = 'deep change in y'
print 'Before change1/change2:'
print 'ns.x=%r, ns.y=%r' %(ns.x, ns.y)
change1(ns.x, py)
print 'After calling change1(ns.x, py):'
print 'ns.x=%r, ns.y=%r' %(ns.x, ns.y)
pz = PNS(ns, 'z')
print '\nNew pointer to non-existent ns.z:\n%r' % pz
print 'Trying to access as yet nonexistent ns.z ...'
try: ns.z
except Exception, e: print '%s: %s'% (e.__class__.__name__,e)
else: print 'ns.z accessed ok'
print 'Passing pz to change(ns.x, pz)...'
change(ns.x, pz)
print 'Result: ns.x=%r, ns.z=%r' %(ns.x, ns.z)
print '\nBefore deleting ns.y via py.v:'
print 'ns.y=%r, py.v=%r' %(ns.y, py.v)
print '\nDeleting ns.y by del py.value ...'
del py.value
print 'Trying to access ns.y ...'
try: ns.y
except Exception, e: print '%s: %s'% (e.__class__.__name__,e)
else: print 'ns.y accessed ok'
print '\nCreating nsother name space to put pz in as a value ...'
nsother = type('AnotherNS',(),{})()
print nsother
nsother.pz = pz
print 'Creating pointer ppz pointing to nsother.pz'
ppz = PNS(nsother,'pz')
print 'ppz = %r'% ppz
print 'ppz.value = %r'% ppz.value
print 'ppz.value.value = %r'% ppz.value.value
print 'ppz.v.v= %r'% ppz.v.v
print '\nDemo read-only pointer to pi in namespace math (the module) ...'
import math
ppi = PNS(math,'pi', 'r') # read only
print 'math = %r' % math
print 'ppi = %r' % ppi
print 'ppi.v = %s' % ppi.v
print '\nAttempting to set math.pi via ppi.v=3.14 (will work via math.pi, BTW !)'
try: ppi.v = 3.14
except Exception, e: print '%s: %s'% (e.__class__.__name__,e)
else: print 'ppi.v set ok: %r' % ppi.v
if __name__ == '__main__':
test()
========================================================================
Result of run:
[10:03] C:\pywk\clp>pns.py
Before change:
ns.x='x value', ns.y='y value'
Making pointer py point to ns.y ...
ptr py=<PNS ptr to 'y' of <__main__.Namespace object at 0x007F9EC0>>
Calling change(ns.x, py) ...
After change:
ns.x='x value', ns.y='new y'
Before change1/change2:
ns.x='x value', ns.y='new y'
After calling change1(ns.x, py):
ns.x='x value', ns.y='deep change in y'
New pointer to non-existent ns.z:
<PNS ptr to 'z' of <__main__.Namespace object at 0x007F9EC0>>
Trying to access as yet nonexistent ns.z ...
AttributeError: 'Namespace' object has no attribute 'z'
Passing pz to change(ns.x, pz)...
Result: ns.x='x value', ns.z='new y'
Before deleting ns.y via py.v:
ns.y='deep change in y', py.v='deep change in y'
Deleting ns.y by del py.value ...
Trying to access ns.y ...
AttributeError: 'Namespace' object has no attribute 'y'
Creating nsother name space to put pz in as a value ...
<__main__.AnotherNS object at 0x007F92C0>
Creating pointer ppz pointing to nsother.pz
ppz = <PNS ptr to 'pz' of <__main__.AnotherNS object at 0x007F92C0>>
ppz.value = <PNS ptr to 'z' of <__main__.Namespace object at 0x007F9EC0>>
ppz.value.value = 'new y'
ppz.v.v= 'new y'
Demo read-only pointer to pi in namespace math (the module) ...
math = <module 'math' (built-in)>
ppi = <PNS ptr to 'pi' of <module 'math' (built-in)>>
ppi.v = 3.14159265359
Attempting to set math.pi via ppi.v=3.14 (will work via math.pi, BTW !)
PNSError: Value write prohibited for <PNS ptr to 'pi' of <module 'math' (built-in)>>
[10:03] C:\pywk\clp>
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

)
You're welcome. Hope this adds another useful angle.
Regards,
Bengt Richter