namespace dictionaries ok?

R

Ron Adam

Hi, I found the following to be a useful way to access arguments after
they are passed to a function that collects them with **kwds.


class namespace(dict):
def __getattr__(self, name):
return self.__getitem__(name)
def __setattr__(self, name, value):
self.__setitem__(name, value)
def __delattr__(self, name):
self.__delitem__(name)

def foo(**kwds):
kwds = namespace(kwds)
print kwds.color, kwds.size, kwds.shape etc....

foo( color='red', size='large', shape='ball', .... etc..)


It just seems awkward to have to use "string keys" in this situation.
This is easy and still retains the dictionary so it can be modified and
passed to another function or method as kwds again.

Any thoughts? Any better way to do this?

Cheers, Ron
 
J

James Stroud

Here it goes with a little less overhead:


py> class namespace:
.... def __init__(self, adict):
.... self.__dict__.update(adict)
....
py> n = namespace({'bob':1, 'carol':2, 'ted':3, 'alice':4})
py> n.bob
1
py> n.ted
3

James

Hi, I found the following to be a useful way to access arguments after
they are passed to a function that collects them with **kwds.


class namespace(dict):
def __getattr__(self, name):
return self.__getitem__(name)
def __setattr__(self, name, value):
self.__setitem__(name, value)
def __delattr__(self, name):
self.__delitem__(name)

def foo(**kwds):
kwds = namespace(kwds)
print kwds.color, kwds.size, kwds.shape etc....

foo( color='red', size='large', shape='ball', .... etc..)


It just seems awkward to have to use "string keys" in this situation.
This is easy and still retains the dictionary so it can be modified and
passed to another function or method as kwds again.

Any thoughts? Any better way to do this?

Cheers, Ron

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com/
 
S

Simon Burton

Yes!

I do this a lot when i have deeply nested function calls
a->b->c->d->e
and need to pass args to the deep function without changing the
middle functions.

In this situation I think i would prefer this variation:

class Context(dict):
def __init__(self,**kwds):
dict.__init__(self,kwds)
def __getattr__(self, name):
return self.__getitem__(name)
def __setattr__(self, name, value):
self.__setitem__(name, value)
def __delattr__(self, name):
self.__delitem__(name)

def foo(ctx):
print ctx.color, ctx.size, ctx.shape

foo( Context(color='red', size='large', shape='ball') )


This is looking like foo should be a method of Context now,
but in my situation foo is already a method of another class.

Simon.
 
R

Ron Adam

James said:
Here it goes with a little less overhead:


py> class namespace:
... def __init__(self, adict):
... self.__dict__.update(adict)
...
py> n = namespace({'bob':1, 'carol':2, 'ted':3, 'alice':4})
py> n.bob
1
py> n.ted
3

James

But it's not a dictionary anymore so you can't use it in the same places
you would use a dictionary.

foo(**n)

Would raise an error.

So I couldn't do:

def foo(**kwds):
kwds = namespace(kwds)
kwds.bob = 3
kwds.alice = 5
...
bar(**kwds) #<--- do something with changed items

Ron
 
R

Ron Adam

Simon said:
Yes!

I do this a lot when i have deeply nested function calls
a->b->c->d->e
and need to pass args to the deep function without changing the
middle functions.

Yes, :) Which is something like what I'm doing also. Get the
dictionary, modify it or validate it somehow, then pass it on. I also
find that when I'm passing variables as keywords,

foo(name=name, address=address, city=city)

I really don't want (or like) to have to access the names with
dictionary key as *strings* in the function that is called and collects
them in a single object.

In this situation I think i would prefer this variation:

class Context(dict):
def __init__(self,**kwds):
dict.__init__(self,kwds)
def __getattr__(self, name):
return self.__getitem__(name)
def __setattr__(self, name, value):
self.__setitem__(name, value)
def __delattr__(self, name):
self.__delitem__(name)
>
def foo(ctx):
print ctx.color, ctx.size, ctx.shape

foo( Context(color='red', size='large', shape='ball') )


This is looking like foo should be a method of Context now,
but in my situation foo is already a method of another class.

Simon.

I didn't see what you were referring to at first. But yes, I see the
similarity.

Cheers,
Ron
 
J

James Stroud

Oops. Answered before I finished reading the question.

James

But it's not a dictionary anymore so you can't use it in the same places
you would use a dictionary.

foo(**n)

Would raise an error.

So I couldn't do:

def foo(**kwds):
kwds = namespace(kwds)
kwds.bob = 3
kwds.alice = 5
...
bar(**kwds) #<--- do something with changed items

Ron

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com/
 
B

Bengt Richter

Yes, :) Which is something like what I'm doing also. Get the
dictionary, modify it or validate it somehow, then pass it on. I also
find that when I'm passing variables as keywords,

foo(name=name, address=address, city=city)

I really don't want (or like) to have to access the names with
dictionary key as *strings* in the function that is called and collects
them in a single object.
Or maybe just add a __repr__ method, if you want to see a readable
representation (e.g., see below).
I didn't see what you were referring to at first. But yes, I see the
similarity.
... def __init__(self,**kwds):
... dict.__init__(self,kwds)
... def __getattr__(self, name):
... return self.__getitem__(name)
... def __setattr__(self, name, value):
... self.__setitem__(name, value)
... def __delattr__(self, name):
... self.__delitem__(name)
... def __repr__(self):
... return 'Context(%s)' % ', '.join('%s=%r'% t for t in sorted(self.items()))
... 'red'

Regards,
Bengt Richter
 
R

Ron Adam

James said:
Oops. Answered before I finished reading the question.

James

Well, the one bad side effect (or feature depending on the
circumstance), is it makes a copy. I wonder if there is a way to modify
the dictionary in place with a function to do the same thing instead of
creating a new object?

Cheers,
Ron
 
R

Ron Adam

James said:
Here it goes with a little less overhead:


py> class namespace:
... def __init__(self, adict):
... self.__dict__.update(adict)
...
py> n = namespace({'bob':1, 'carol':2, 'ted':3, 'alice':4})
py> n.bob
1
py> n.ted
3

James

How about...

class namespace(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__


This seems to work, and eliminates the indirect method calls.

Cheers,
Ron
 
S

Steven D'Aprano

Simon Burton wrote:

Yes!

I do this a lot when i have deeply nested function calls
a->b->c->d->e
and need to pass args to the deep function without changing the
middle functions.

If you find yourself passing arguments to functions
just so they can in turn pass them on to other
functions, you probably shouldn't be using deeply
nested function calls and should be looking for another
program structure.
 
D

Duncan Booth

But it's not a dictionary anymore so you can't use it in the same places
you would use a dictionary.

foo(**n)

Would raise an error.

So I couldn't do:

def foo(**kwds):
kwds = namespace(kwds)
kwds.bob = 3
kwds.alice = 5
...
bar(**kwds) #<--- do something with changed items
I agree with Steven D'Aprano's reply, but if you are set on it you could
try this:
def __init__(self, *args, **kw):
dict.__init__(self, *args, **kw)
self.__dict__ = self

n = namespace({'bob':1, 'carol':2, 'ted':3, 'alice':4})
n.bob 1
n.bob = 3
n['bob']
3

The big problem of course with this sort of approach is that you cannot
then safely use any of the methods of the underlying dict as they could be
masked by values.

P.S. James, *please* could you avoid top-quoting.
 
R

Ron Adam

Duncan said:
But it's not a dictionary anymore so you can't use it in the same places
you would use a dictionary.

foo(**n)

Would raise an error.

So I couldn't do:

def foo(**kwds):
kwds = namespace(kwds)
kwds.bob = 3
kwds.alice = 5
...
bar(**kwds) #<--- do something with changed items

I agree with Steven D'Aprano's reply, but if you are set on it you could
try this:


def __init__(self, *args, **kw):
dict.__init__(self, *args, **kw)
self.__dict__ = self


n = namespace({'bob':1, 'carol':2, 'ted':3, 'alice':4})
n.bob
1
n.bob = 3
n['bob']

3

The big problem of course with this sort of approach is that you cannot
then safely use any of the methods of the underlying dict as they could be
masked by values.

P.S. James, *please* could you avoid top-quoting.

Or worse, the dictionary would become not functional depending on what
methods were masked.


And this approach reverses that, The dict values will be masked by the
methods, so the values can't effect the dictionary methods. But those
specific values are only retrievable with the standard dictionary notation.

class namespace(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__

n = namespace()
n.__getattr__ = 'yes' # doesn't mask __getattr__ method.

print n['__getattr__'] -> 'yes'

The value is there and __getattr__() still works. But n.__getattr__
returns the method not the value.

So is there a way to keep the functionality without loosing the methods?


BTW, I agree with Steven concerning data structures. This really isn't
a substitute for a data structure. Many keys will not work with this.

n.my name = 'Ron'
n.(1,2) = 25
n.John's = [ ... ]

The use case I'm thinking of is not as a shortcut for data structures,
but instead, as a way to keep names as names, and maintaining those
names in a group. Thus the namespace association.

def foo(**kwds):
kwds = namespace(kwds)
print kwds.name
print kwds.value
...

name = 'ron'
value = 25
foo( name=name, position=position )

Cheers,
Ron
 
J

James Stroud

P.S. James, *please* could you avoid top-quoting

Were it not for Steve Holden's providing me with a link off the list, I would
have never known to what it is you are referring. I have read some relevant
literature to find that this is more widely known as "top-posting". I'll go
with majority rules here, but I would like to say that my lack of
"netiquette" in this matter comes from practicality and not malice. I have
read many a letter both in-line quoted and/or top-posted and have never
really made a distinction between the two. Both styles have been very easy
for me to follow in the past using my mouse wheel (first added to the mouse
device circa 1995, when a 486/DX33 was still considered a powerful machine).
I'm sorry if I can't find a dumb terminal for a "VAX" with which to read my
email. Perhaps, if i could, I would understand your frustration a little
better.

The only reason I'm making a point of this is that many people come to python
from fields other than computer science or hacker-ology. Posting styles will
vary considerably among these people, so deal with the deviants carefully.
Their differences in styles, like mine, probably arise from the culture of
their respective fields. Most, like me, may not even know what the heck you
are talking about. Also, here is a well written synopsis of the arguments in
favor of top-posting and they may even be strong enough to legitimize the
practice:

http://alpage.ath.cx/toppost/toppost.htm

In light of these arguments, I hereby reserve the right to revert to
top-posting if the compulsion overwhelms me.

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com/
 
B

Bengt Richter

Duncan said:
Ron said:
James Stroud wrote:

Here it goes with a little less overhead:

But it's not a dictionary anymore so you can't use it in the same places
you would use a dictionary.

foo(**n)

Would raise an error.

So I couldn't do:

def foo(**kwds):
kwds = namespace(kwds)
kwds.bob = 3
kwds.alice = 5
...
bar(**kwds) #<--- do something with changed items

I agree with Steven D'Aprano's reply, but if you are set on it you could
try this:

class namespace(dict):

def __init__(self, *args, **kw):
dict.__init__(self, *args, **kw)
self.__dict__ = self


n = namespace({'bob':1, 'carol':2, 'ted':3, 'alice':4})
n.bob
1

n.bob = 3
n['bob']

3

The big problem of course with this sort of approach is that you cannot
then safely use any of the methods of the underlying dict as they could be
masked by values.

P.S. James, *please* could you avoid top-quoting.

Or worse, the dictionary would become not functional depending on what
methods were masked.


And this approach reverses that, The dict values will be masked by the
methods, so the values can't effect the dictionary methods. But those
specific values are only retrievable with the standard dictionary notation.

class namespace(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__

n = namespace()
n.__getattr__ = 'yes' # doesn't mask __getattr__ method.

print n['__getattr__'] -> 'yes'

The value is there and __getattr__() still works. But n.__getattr__
returns the method not the value.

So is there a way to keep the functionality without loosing the methods?


BTW, I agree with Steven concerning data structures. This really isn't
a substitute for a data structure. Many keys will not work with this.

n.my name = 'Ron'
n.(1,2) = 25
n.John's = [ ... ]

The use case I'm thinking of is not as a shortcut for data structures,
but instead, as a way to keep names as names, and maintaining those
names in a group. Thus the namespace association.

def foo(**kwds):
kwds = namespace(kwds)
print kwds.name
print kwds.value
...

name = 'ron'
value = 25
foo( name=name, position=position )
Just had the thought that if you want to add bindings on the fly modifying the
original object, you could use the __call__ method, e.g.,
... __getattr__ = dict.__getitem__
... __setattr__ = dict.__setitem__
... __delattr__ = dict.__delitem__
... def __call__(self, **upd):
... self.update(upd)
... return self
... -- showing {'initial': 1}
{'initial': 1}

And updating with a second keyword on the fly:
-- showing {'initial': 1}
-- showing {'second': 2, 'initial': 1}
{'second': 2, 'initial': 1}

FWIW ;-)

Regards,
Bengt Richter
 
M

Mike Meyer

I'm sorry if I can't find a dumb terminal for a "VAX" with which to read my
email. Perhaps, if i could, I would understand your frustration a little
better.

You don't need a VAX. All it takes is trying to dethread a top-posted
conversation. I've given up in frustration on more than one such
venture.
The only reason I'm making a point of this is that many people come to python> from fields other than computer science or hacker-ology. Posting styles will
vary considerably among these people, so deal with the deviants carefully.

I belive Duncan did so. He asked you to "please* avoid to
top-posting".
Their differences in styles, like mine, probably arise from the culture of
their respective fields. Most, like me, may not even know what the heck you
are talking about. Also, here is a well written synopsis of the arguments in
favor of top-posting and they may even be strong enough to legitimize the
practice:
http://alpage.ath.cx/toppost/toppost.htm

Well, if I could find a 300 baud modem, I might understand his
objection to not top-posting (he really only has one). His comments
about why people shouldn't complain about top-posting are full of
suggestions that don't generally work on netnews and mail, though they
may be valid elsewhere, and his suggestion about when to top-post
leaves out the *very important* criteria that you don't expect to read
any replies to your comment, and don't care if you make life difficult
for those who have different expectations.

He's right about inadequately-trimmed repklies, though. Not being on a
300 baud modem, I don't think they're as annoying as top-posting, but
they are certainly something that we can do without.
In light of these arguments, I hereby reserve the right to revert to
top-posting if the compulsion overwhelms me.

That's your right. Be aware that people will ignore, correct and/or
complain about you doing so.

<mike
 
R

Ron Adam

Bengt said:
Or worse, the dictionary would become not functional depending on what
methods were masked.


And this approach reverses that, The dict values will be masked by the
methods, so the values can't effect the dictionary methods. But those
specific values are only retrievable with the standard dictionary notation.

class namespace(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__

n = namespace()
n.__getattr__ = 'yes' # doesn't mask __getattr__ method.

print n['__getattr__'] -> 'yes'

The value is there and __getattr__() still works. But n.__getattr__
returns the method not the value.

So is there a way to keep the functionality without loosing the methods?


BTW, I agree with Steven concerning data structures. This really isn't
a substitute for a data structure. Many keys will not work with this.

n.my name = 'Ron'
n.(1,2) = 25
n.John's = [ ... ]

The use case I'm thinking of is not as a shortcut for data structures,
but instead, as a way to keep names as names, and maintaining those
names in a group. Thus the namespace association.

def foo(**kwds):
kwds = namespace(kwds)
print kwds.name
print kwds.value
...

name = 'ron'
value = 25
foo( name=name, position=position )

Just had the thought that if you want to add bindings on the fly modifying the
original object, you could use the __call__ method, e.g.,
... __getattr__ = dict.__getitem__
... __setattr__ = dict.__setitem__
... __delattr__ = dict.__delitem__
... def __call__(self, **upd):
... self.update(upd)
... return self
...-- showing {'initial': 1}
{'initial': 1}

And updating with a second keyword on the fly:
-- showing {'initial': 1}
-- showing {'second': 2, 'initial': 1}
{'second': 2, 'initial': 1}

FWIW ;-)

Regards,
Bengt Richter

Getting better! ;-)

That (or something similar) might avoid copying the whole thing in some
situations, which is something I am concerned about. But how to change
a dict to a namespace without copying the contents item by item?

I'm not sure where or if this is going anywhere. It may tie back into
the properties groups example (see below) I posted earlier and keep
finding improvements for. ;-)

cheers,
Ron




""" GPobject.py

Grouped Properties Object:

This need has presented itself while programming Tkinter
applications where a *LOT* of keywords are used. I think
property groups would also be good for general interface
building. The external view is of a single cohesive
object, while the internal mechanism keeps the attributes
grouped so they can be forwarded easily as **kwds.

class foo(GPobject):
def __init__(self):
self.properties(name, item_1, item_2, ... item_n)
def getter():
...
def setter():
...
def remover():
...
self.name.doc( __doc__ string )
self.name.get = getter
self.name.set = setter
self.name.remove = remover

* The properties() method can also accept a dictionary.
This will set both the names and the values at the same time.

self.properties(name, dictionary)

class myclass(GPobject):
def __init__(self, **kwds):
self.properties('kwds', **kwds)

* group.setup() allows easy initiation of get, set, remove,
and doc group settings in one step.

self.properties( name, *members )
self.name.setup( get=myget, set='set', del='del',
doc='a property group' )

* Using string flags to indicate default values lets you
shorten the expression further.

self.properties( name, *members )
self.name.setup( myget, 'set', 'del', 'a property group')


The following is only somewhat tested... but it seems to work.
"""

class GPdict(dict):
doc = "a property group"
def doc(self, docstring): self.__doc__ = docstring
def get(self, item): return self[item]
def set(self, item, value): self[item] = value
def remove(self, item): del self[item]
def setup( self, fget='get', fset='set',
fdel='del', doc=doc ):
if fget != 'get': self.get = fget
if fset != 'set': self.set = fset
if fdel != 'del': self.remove = fdel
self.__doc__ = doc

# Some useful common alternate methods to
# replace get, set and remove.
def readonly(self, *args):
raise AttributeError, 'read only property'
def setonce(self, item, value):
if self[item] is None: self[item] = value
else:
raise AttributeError, 'set once property'
def nonremovable(self, *args):
raise AttributeError, 'non removable property'

class GPobject(object):
""" an object that can use grouped properties """
__properties__ = {}
def __new__(cls, *args, **kwds):
cls.__properties__ = {}
return object.__new__(cls, *args, **kwds)
def properties(self, *args, **kwds):
dct = GPdict()
for i in args[1:]:
dct.setdefault(i,None)
dct.update(kwds)
self.__properties__[args[0]] = dct
self.__dict__[args[0]] = dct
def __getattr__(self, name):
for dct in self.__properties__:
if name in self.__properties__[dct]:
return self.__properties__[dct].get(name)
return self.__dict__[name]
def __setattr__(self, name, value):
notprop = True
for dct in self.__properties__:
if name in self.__properties__[dct]:
self.__properties__[dct].set(name,value)
notprop = False
if notprop:
self.__dict__[name] = value
def __delattr__(self, name):
for dct in self.__properties__:
if name in self.__properties__[dct]:
self.__properties__[dct].remove(name)
return
del self.__dict__[name]



=================
### Properties as keyword sorter example,
### (sort to groups, not ordered sort)

class sorter(GPobject):
text_group = str.split('text fill font')
line_group = str.split('line fill arrows')
def __init__( self, text=None, fill=None, line=None,
arrows=None, font=None ):
self.properties('_text', *self.text_group)
self.properties('_line', *self.line_group)
self.text = text
self.fill = fill
self.line = line
self.arrows = arrows
self.font = font

s = sorter(text='hello', fill='black', line='solid', arrows='both')
print 's._text =', s._text
print 's._line =', s._line
print s.__properties__
 
J

James Stroud

That's your right. Be aware that people will ignore, correct and/or
complain about you doing so.

If I may be a complete ass: That should be "correct and/or complain about
*your* doing so."

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com/
 
S

Steve Holden

James said:
If I may be a complete ass: That should be "correct and/or complain about
*your* doing so."

You *may* be a complete ass. But you don't *appear* to be :)

now-let's-get-back-to-the-python-ly y'rs - steve
 
S

Steven D'Aprano

Well, if I could find a 300 baud modem, I might understand his
objection to not top-posting (he really only has one).

Perhaps he is hosting his website on a server with a 300 baud modem? It
times out for me... *wink*

A: Because it messes up the order in which people read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying email habit?


I wouldn't object to top-posting nearly so much if the average top-poster
had the writing skills of an illiterate sloth hunter in the jungles of
Borneo[1], but sadly most of them don't. I get people replying to an email
three weeks after I sent them the original, and they top-post the answer
"Yes, I agree". When I read it, my brain reboots from the sheer inanity of
it. You agree to *what*? Do I really care enough to read through the
entire thread -- which you've so *thoughtfully* quoted in its entirety,
eight levels deep, thank you very much -- to find out what you are
agreeing to?

And then when I do read through it, I discover that what was actually
asked was three or four *specific* questions like "How much memory would
you like your new PC to have?" and "Would you like wireless?".

Top-posting encourages stupid mistakes like that, by encouraging people to
hit send after they've said the first thing they thought of, without
actually reading through the email to see if there are other issues that
need to be dealt with.





[1] There are no sloths in Borneo.
 
S

Simon Burton

In my case the deeply nested function calls are recursive calls for a
tree traversal.
This is similar to the visitor design pattern, where the Context class
above is the Visitor. The difference is that the Context instance does
not "visit" or "act" upon the nodes, but just stores state/context
information about the current traversal.

Simon.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,269
Messages
2,571,338
Members
48,026
Latest member
DannieKeeg

Latest Threads

Top