Victor Subervi wrote:
On Sun, Nov 29, 2009 at 10:23 PM, Dave Angel <da...@ieee.org> wrote:

exec is a statement, and statements don't have "return values."   It's not
a function, so there are no parentheses in its syntax, either.  exec is also
a technique of last resort;  there's nearly always a better/safer/faster way
to accomplish what you might want, but of course you don't say what that is.

As for "returning" values, exec by default uses the same global space as
your app, so you can just modify a global variable in your "called" code and
use it afterwards.

abc = 42
value = 12
exec "abc = %d" % value
print abc


Taking out the parenthesis did it! Thanks. Now, you state this is an option
of last resort. Although this does indeed achieve my desired aim, here is a
complete example of what I am trying to achieve. The following is from
'createTables2.py':

  for table in tables:
    try:
      exec 'from options import %s' % table
    except:
      pass
    try:
      exec '%s()' % table
    except:
      pass


The following is from 'options.py':

def jewelry(which=''):
  code = []
  names = []
  meanings = []
  code.append(['5', '5&frac12;', '6', '6&frac12;', '7', '7&frac12;', '8',
'8&frac12;', '9', '9&frac12;', '10', '10&frac12;', '11', '11&frac12;', '12',
'12&frac12;', '13', '13&frac12;'])
  meanings.append('The standard ring sizes.')
  names.append('ringSizes')
  code.append(['Petite (7&#34;)', 'Average (7&frac12;&#34;)', 'Large
(8&#34;)', 'Extra-large (8&frac12;&#34;)'])
  meanings.append('The standard bracelet sizes.')
  names.append('braceletSizes')
  code.append(['16&#34;', '18&#34;', '20&#34;', '22&#34;', '24&#34;'])
  meanings.append('The standard necklace sizes.')
  names.append('necklaceSizes')
  code.append(['14K gold', '18K gold', 'silver', '14K white gold', '18K
white gold', 'platinum', 'tungsten', 'titanium'])
  meanings.append('The standard jewelry metals.')
  names.append('metals')
  code.append(['diamond', 'emerald', 'ruby', 'sapphire', 'pearl', 'opal',
'topaz', 'onyx', 'lapiz lazuli', 'tanzanite', 'garnet', 'quartz', 'rose
quartz', 'amethyst', 'alexandrite', 'peridot', 'tourmaline', 'citrine',
'turquoise'])
  meanings.append('The standard jewelry stones.')
  names.append('stones')
  if which == '':
    i = 0
    all = ''
    while i < len(meanings):
      table = '%s\n' % meanings[i]
      table += "<table>\n <tr>\n  <td colspan='8' align='center'>%s</td>\n
</tr>" % names[i]
      j = 0
      for elt in code:
        if (j + 8) % 8 == 0:
          table += ' <tr>\n'
        table += '  <td>%s</td>\n' % code[i]
        if (j + 8) % 8 == 0:
          table += ' </tr>\n'
        j += 1
      if table[-6:] != '</tr>\n':
        table += ' </tr>\n'
      table += '</table>\n'
      all += table + '<br /><br />'
      i += 1
    print all

This all works fine; however, if there is a better way of doing it, please

let me know.
Thanks,
V

The parentheses can't do any harm for that particular expression, so that wasn't your problem. But I'm glad you fixed whatever else was your problem. I mentioned it because sometimes they can cause problems, and you shouldn't get in bad habits. (things like if, return, and exec are all statements that take an expression, but do not need parentheses.)

I'll throw in a comment here about a bare except. Also a bad idea. You could easily mask some other problem involved in the import, and the program silently continues. If you know of a specific problem, or category of problems that you want to ignore, then pick an exception, or tuple of exceptions, to do that.


The immediate question is how to get rid of exec. You're using it two places. First case, you're just using it to extract specific objects from that module's namespace.

Assuming you've already imported options earlier in the code, you can replace
   from options import xyzzy
by
   optfunc = options.xyzzy
or
   option_func = getattr(options, "xyzzy", None)
or even
   option_func = getattr(options, "xyzzy", dummyfunc)
(where dummyfunc() is a function which takes no arguments and does nothing)

Now, assuming you plan to call each such function immediately, you can just say
   option_func()
in the same loop.


def  dummyfunc():
    pass

....
   for table in tables:
        option_func = getattr(options, table, dummyfunc)
        option_func()

If you didn't have the dummyfunc, you'd need an "if option_func" in there.


Naturally, if this "tables" comes from the user, you need to give him some feedback, so perhaps dummyfunc isn't an empty function after all, but is supplied in options.py

Other comments about your code: Someone else has mentioned names, so I won't dwell on that.

     if table[-6:] != '</tr>\n':

should use endswith(), and you won't run the risk of counting wrong if your literal changes.

       if (j + 8) % 8 == 0:

could be simpler:

       if j % 8 == 0:

The pattern:
           j=0

     for elt in code:
should be replaced by:
     for j, elt in enumerate(code):

(and of course don't forget to then remove the j+=1 )

You're using the indexing variable i when it'd make much more sense to use zip. Or perhaps meanings and code should be a single list, with each item being a tuple. That's what zip builds, after the fact. But if you build it explicitly, you're less likely to accidentally have an extra or missing element in one of them, and have to deal with that bug.

The zip approach might look something like:
   for meaning, code_element in zip(meanings, code):

and then whenever you're using meanings[i] you use meaning, and whenever you're using code[i], you use code_element. (Obviously name changes would help, since code is a list, but the name isn't plural)



More generally, when I see code with that much data embedded in it, it cries out for templates. They're known by many different names, but the idea is to write your data structures somewhere else, and manipulate them with the code, instead of embedding it all together. I suspect that each of these functions looks very similar, and they each have their own set of bugs and glitches. That's a good indicator that you need to separate the data.

For my web work, I've written my own, very simple templating logic that's only as good as I needed. So I can't comment on the various ones that are already available. But the idea is you put data into one or more text files, which you systematically manipulate to produce your end result. A particular text file might be comma delimited, or defined in sections like an .ini file, or both, or some other mechanism, such as xml. But make it easy to parse, so you can concentrate on getting the data into its final form, consistently, and with common code for all your tables, parameterized by the stuff in the data files.

DaveA

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to