[Tutor] Changing the Attribute of a Variable

Marc Tompkins marc.tompkins at gmail.com
Wed Feb 18 10:00:26 CET 2009


On Tue, Feb 17, 2009 at 7:09 PM, Wayne Watson
<sierra_mtnview at sbcglobal.net>wrote:

I have a few notes, but first I just have to say: you're working very hard
to implement an ordered dictionary, and you really, really don't need to.
Python's (unordered) dictionaries work just fine for reading and writing
configurations; you just need to rethink a little.  Or, try ConfigObj, which
reads and writes your config files exactly according to your specification
with very little fuss and minimal wheel-reinvention.  'Nuff said.

Ok, let's see how this works. I've defined this function and the
> config_var_list. stop time is the last entry shown. It is part of the
> constructor for mainloop, Sentinel_GUI. and loads sdict as shown.
>         def Set_ConfigDictionary():
>
>             *config_var_list* = (['config_file_name',  ['Initial.sen',
> STR]],
>                 ['mask_file_name',  ['xyz', STR]],
>                     ...
>                 ['stop_time',  ['datetime.time(6, 0, 0)', DAT]],
>                         ...
>                 )
>             # load sdict
>             *sdict* = {}
>             for j in range(0, len(config_var_list)):
>             #    print "j: ", j, "str part: ", str(config_var_list[j][0]),
> config_var_list[j][1]
>                 sdict[str(config_var_list[j][0])] =
> config_var_list[j][1][0]
>                       ...
>

These two lines are unnecessary:
            *self.sdict = sdict*
            *self.config_var_list* = config_var_list
You don't need to create and load "sdict" and "config_var_list" before
assigning them to self - you could start out with
            *self.sdict* = {} and *self.config_var_list* = etc.
and work from there.

                      ...
> sdict and config_var_list become global to Sentinel_GUI. The first index of
> conf_var_list maintains order for sdict. That is, when I want to output the
> config file, I use to to fetch from sdict what I need, which is a value and
> the "user" type, i.e., STR, DAT, BOO, etc.
>

I don't understand the need for STR, DAT, BOO - if you're type-checking, you
don't need to create your own types to do it, and if they're informational
only, you could just give your variables descriptive names.

Relax and learn to love Python's iterators.  You don't need an index to step
through the members of a list or tuple - Python is happy to give them to you
one at a time and in order!
Instead of:
            for j in range(0, len(config_var_list)):
                sdict[str(config_var_list[j][0])] = config_var_list[j][1][0]
try this:
            for var in config_var_list:
                sdict[var[0]] = var[1][0]

Also - config_var_list is a tuple of lists.  (I'm guessing you intended to
make it a list of lists - that's what the name indicates, after all - but
putting it between "( )" makes it a tuple.)  Myself, I'd make it either a
list of dictionaries (preserves ordering), or a dictionary of dictionaries.
Here's what I'm talking about -
            *config_var_list* = [{"name":"config_file_name",
"value":"Initial.sen", "type": "STR"},
                {"name":"mask_file_name",  "value":"xyz", "type":"STR"},
                    ...
                {"name":"stop_time",  "value":"datetime.time(6, 0, 0)",
"type":"DAT"},
                        ...
                ]
            # load sdict
            *sdict* = {}
            for var in config_var_list:
                sdict[var["name"]] = var["value"]

I just find it much, much easier to understand what's going on - with [0]
and [1][0] I had to read the code four or five times to grok it.  I haven't
benchmarked the two approaches - it's possible that access by list indices
is faster - but it's hard to overestimate the importance of clarity.

Now in SaveConfigFile, I go merrily along thusly:
>
>                    ...
>         #                         SAVE CONFIG FILE
>         items = self.sdict.keys()
>         items.sort()
>         for (j, conf_entry) in enumerate(self.config_var_list):
>             varName = conf_entry[0]
>             varType = self.config_var_list[j][1][1]
>             # Note, date-time vars are in hh:mm:ss
>             varValue = eval('self.' + varName)
>             var_assignment = varName + "=" + str(varValue)    <<--- Beep,
> beep
>             config_file.write(var_assignment + "\n")
>                          ...
>

Don't work so hard!  You're using enumerate() to get both the index and the
item - but you don't need to, and it's easier to use and to read if you
don't.  In other words,
            varType = self.config_var_list[j][1][1]
could be shortened to
            varType = conf_entry[1][1]
You already did that for varName - just take it the rest of the way.

Next, that eval() - better this way:
            varValue = getattr(self, varName)
There _are_ some valid use cases for eval() - but getting and setting
attributes ain't it.

So, taking just this section in isolation (assuming you don't implement any
of my other suggestions) -
        for conf_entry in enumerate(self.config_var_list):
            varName = conf_entry[0]
            varType = conf_entry[1][1]
            varValue = getattr(self, varName)
            var_assignment = varName + "=" + str(varValue) + "\n"
            config_file.write(var_assignment)



> "Beep, beep" shows the likely problem.
>

You haven't actually explained what the "problem" is, but I'm going to take
a stab at it...  datetime objects are not strings.  They are - how shall I
put this - datetime objects.  You can't actually write them to a
configuration file (you could pickle/shelve them, but that's a topic for
another time) - you can only write a string representation to the file.
When you want to read them back, you need to convert them back from a string
into a datetime.time; this is called "round-tripping".  The complementary
function to strftime() is called strptime():
>>>tstTime = datetime.time(9,10,11)
>>>outStr = tstTime.strftime("%H:%M:%S")  # produces a string
>>>print outStr
'09:10:11'
>>>inTime = datetime.datetime.strptime(outStr, "%H:%M:%S") # creates a
datetime object from our string
>>>print inTime
1900-01-01 09:10:11  # if no date is specified, datetime objects default to
1 Jan 1900 (on Windows XP, anyway)
>>>print inTime.time()  # just the time portion
09:10:11



> Here, I suspect that I should put out something like datetime.time(11, 20,
> 10). The time being 11:00:10. Instead I chose, to put 11:00:10 in the file.
>

Please, please, please don't put "datetime.time(11, 20, 10)" in your config
file.  Apart from being hard on the eyes, it pretty much requires that you
use eval() - a terrible, terrible idea.  If somebody edits your config file
by hand, and either intentionally or accidentally puts something funky in
there, you want the worst possible consequence to be an error message, not
disaster.
You could store "(11,20,10)", but it's probably clearer to store the time in
a human-readable format and convert it back with strptime() as above.

Hope that helps...
-- 
www.fsrtechnologies.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20090218/2b9f9dbd/attachment.htm>


More information about the Tutor mailing list