Exec Statement Question

Victor Subervi victorsubervi at gmail.com
Mon Nov 30 18:30:31 CET 2009


On Mon, Nov 30, 2009 at 1:12 PM, Dave Angel <davea at ieee.org> wrote:

> Victor Subervi wrote:
>
>> On Sun, Nov 29, 2009 at 10:23 PM, Dave Angel <davea at 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")', 'Average (7&frac12;")', 'Large
>> (8")', 'Extra-large (8&frac12;")'])
>>  meanings.append('The standard bracelet sizes.')
>>  names.append('braceletSizes')
>>  code.append(['16"', '18"', '20"', '22"', '24"'])
>>  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.
>
>
Yeah, maybe on the next iteration after I've finished the shopping cart and
got it working. I'm the one who does everything right now, so it's not an
issue for me.
Thanks,
V
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20091130/e5f17678/attachment.html>


More information about the Python-list mailing list