[Tutor] dictionary dispatch for object instance attributes question

Brian van den Broek bvande at po-box.mcgill.ca
Wed Feb 16 05:48:31 CET 2005


Jeff Shannon said unto the world upon 2005-02-15 21:20:
> On Tue, 15 Feb 2005 17:19:37 -0500, Brian van den Broek
> <bvande at po-box.mcgill.ca> wrote:
> 
> 
>>My Node class defines a _parse method which separates out the node
>>header, and sends those lines to a _parse_metadata method. This is
>>where the elif chain occurs -- each line of the metadata starts with a
>>tag like "dt=" and I need to recognize each tag and set the
>>appropriate Node object attribute, such as .document_type. (I do not
>>want to rely on the unhelpful names for the tags in the file format,
>>preferring more self-documenting attribute names.)
> 
> 
> In addition to using setattr(), I'd take a slightly different approach
> to this.  (This may just be a matter of personal style, and is not as
> clearly advantageous as using setattr() instead of exec, but...)

Hi Jeff and all,

I am *pretty* sure I followed what you meant, Jeff. Thank you for the 
suggestions! I don't think they will fit with my situation, but that I 
think so might say more about my present level of understanding of OOP 
and design issues than about either the suggestions or the situation. :-)

> .class Node(object):
> .    metadata = {'dt': 'document_type', 'something': 'some_other_field', ...}
> .    def __init__(self):  # ....
> .    def update(self, **kwargs):
> .        for key, value in kwargs.items():
> .        try:
> .            attr_name = self.metadata[key]
> .        except KeyError:
> .            raise ValueError("Invalid field type '%s'" % key)
> .        setattr(self, attr_name, value)
> 
> For starters, I've made metadata a class attribute rather than an
> unconnected dictionary.  This seems conceptually nicer to me.

The problem is that my Node instance live in a TP_file class instance, 
and the way my code is now, the TP_file instance also needs to see the 
metadata dict. There are a few tags, which if present in any Node of 
the file make me want to treat the entire file a bit differently. (Of 
course, here is the place where my novice-designer status is most 
likely to be bitting me.) So, that's why I have it as a module level 
object, rather than within a class. (I do, however, see your point 
about it being neater.)


> In addition, update() can now modify several attributes at once, at
> the cost of a bit of extra parsing up front.
> 
> Supposing that your node header looks like this:
> 
> .header = "dt=text/plain;something=some_value;last=some_other_thing_here"
> 
> Now, we can break that into fields, and then split the fields into a
> name and a value --
> 
> .tags = {}
> .for field in header.split(';'):
> .    name, value = field.split('=')
> .    tags[name] = value
> .
> .n = Node()
> .n.update(**tags)
> 
> You can even simplify this a bit more, by rewriting the __init__():
> 
> .    def __init__(self, **kwargs):
> .        if kwargs:
> .            self.update(**kwargs)
> 
> Now you can create and update in a single step:
> 
> .n = Node(**tags)

The metadata all occurs one element to a line in my original file. 
I've got the TP_file class breaking the nodes up and sending the 
contents to new Node instances (as Alan suggested in my previous 
thread). The Node instance has a parse method that reads the node 
contents line by line and sends the appropriate lines to the 
parse_metadata method. (All lines before a designated `header-ending' 
line.) Maybe I'm still missing a better way, but as I am processing 
line by line, each line with one element, I don't see how to use this 
cool looking multiple elements at once approach. (The other 
complication that I didn't mention is that the parse_metadata method 
has to do more than just store the metadata -- some elements must be 
converted to ints, others left as strings, and still others can have 
multiple instances in a single Node, so rather than be set they must 
be appended to an attribute list, etc. The setattr way has taken me 
from 20 elifs to just 4, though :-) )

At any rate, my whole code is (perhaps wrongly) organized around 
logical-line based processing.

> You could also put all of the splitting into fields in a method, and
> when __init__ gets a single string as its argument simply pass it to
> that method and update with the results...
> 
> --Jeff Shannon

Anyway, such are the reasons I'm not sure the suggestions will work in 
my situation. I'm glad to have seen them, though, and am going to save 
them for the point where I actually have the whole program working and 
can think about large-scale refactoring. I may well then find that my 
current uncertainty is unwarranted. But I'd like to make the beast 
live before I make it thrive :-)

Thanks again, and best,

Brian vdB



More information about the Tutor mailing list