On Thu, Sep 3, 2009 at 4:30 PM, Steven D'Aprano wrote:On Thu, 03 Sep 2009 15:46:01 -0700, Ken Newton wrote:
I have created the following class definition with the idea of making a
clean syntax for non-programmers to created structured data within a
python environment.
What do you expect non-programmers to do with this class, without
programming? How can they even use it?
A couple extra details: The users are scientists, and technically
sophisticated.
They are used to specifying complicated sets of parameters and this
structured
form matches the way they are used to thinking about their work. Though they
are not programmers, they can define expressions to represent the desired
calculations for this project and occasionally write small programs in the
course
of their work. However, they will not like a long learning curve and will
want to be
isolated from most of the details in good programming design (error
handling,
creating supporting infrastructure, etc.) In this case, Python will be used
as
a scripting language with most of the details specified by simple function
expressions
or defining structured data or specifying (linear) sequences of actions.
...
The expected use would have all items in the structure be simple python
types or AttrClass types. Code written in python could walk the
structure in a simple way to locate any desired values. Code in a C/C++
extension should also be able to walk the structure to use any value in
the structure.
I don't see the purpose of it. Nested data structures are generally
harder for people to understand than non-nested ones, hence the Python
Zen: "flat is better than nested". For example, a list is easier to grasp
than a tree. This especially applies to non-programmers.
I don't see how this is simple enough for non-programmers, or useful for
programmers. For programmers, just nest objects:
class Parrot(object):
pass
obj = Parrot()
obj.x = 1
obj.y = Parrot()
obj.y.z = 2
obj.y.z.zz = Parrot()
This is close to what I started with when I started exploring this idea.
But I felt I needed a way to create some additional support, that is
the str and repr functions. These structures will be created and
populated once and saved in a python source file, which will be
loaded from the embedding C++ program. The (scientist) users will
need a way to review the structure and current values while
interactively changing a few of the values to observe their effect on
the system. My recursive __str__() and __repr__() functions were
intended to provide that support.
class AttrClass(object):
"""AttrClass lets you freely add attributes in nested manner"""
You don't actually need a special class for that, unless you're providing
extra functionality. A bare subclass of object will let you freely add
attributes in a nested manner.
It seems to me that you're combining two different sets of functionality,
but only describing one. The first is, nested attributes -- but you get
that for free with any class. The second is the ability to set (but
strangely not retrieve!) attributes using dictionary-like syntax. But
that's implied by the code, you haven't mentioned it in your description
or documentation.
def __init__(self):
pass
If the __init__ method doesn't do anything, you don't need it.
Good point. This was a placeholder. My intention was to add some
code to initialize the class from a dict. But didn't get around to
implementing that. Or the ideas I tried (couple years ago) didn't work.
def __setitem__(self, key, value):
return self.__dict__.__setitem__(key, value)
Simpler to just say:
def __setitem__(self, key, value):
self.__dict__[key] = value
You don't strictly need the return, because methods return None by
default, and dict.__setitem__ always returns None (unless it raises an
exception).
Now, I can't remember why I ended up with my strange syntax. Unless
it was related to the fact that I started doing this in IronPython and this
made it work better in that context or on the C# side of things. I'm now
planning this for a CPython embedding situation. And, perhaps as you
pointed out, the __setitem__ isn't needed at all in the code I showed.
This method allows you to set attributes using dict syntax:
instance['key'] = 123 # same as instance.key = 123
But you don't have corresponding __getitem__ or __delitem__ methods, so
you can't do these:
value = instance['key'] # fails
del instance['key'] # fails
The getitem and delete behaviors are not expected to be common uses.
But if I continue with this approach, should be implemented for
completeness.
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__,
self.__dict__.__repr__())
This strongly implies that you should be able to create a new instance
using that format:
AttrClass({'key': 123, 'another_key': 456})
but that fails. As a general rule, the repr() of a class should (if
possible) work correctly if passed to eval(). This doesn't. That's not
entirely wrong, but it is unusual and confusing.
Yes, as mentioned above, I would like to initialize from a dict. Then the
given repr syntax would be a better fit. Appending a dict or concatenating
two or more objects might also be useful. I'd also consider serializing to
an XML file, or a variation of the ConfigFile or *.ini file formats if the
data
is kept to only a depth of 2 levels.
--
Steven
Thanks for all your comments.
Ken