Cherniavsky Beni <c...@users.sf.net> added the comment:

Hi Steven.
Please confirm if we can mark the bug closed; if you need farther advice, 
posting your full code (not just the error case) on comp.lang.python or 
StackOverflow would be more effective.

The documentation is indeed correct but hard to find (you're not the first to 
be surprised by UnboundLocalError); I'm working on making things more 
discoverable in issue 4246.
See also 
http://docs.python.org/py3k/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

First of all, it's important to understand that a Python function has a *fixed* 
set of local variables, frozen when the function is parsed.  If you assign to 
it (e.g. ``name = None``), *all* appearances of the name in the function refer 
to a local variable; if not, they refer to the outer scope.
Therefore, you can't achieve what you want with local variables.

Generally, dynamically creating variables is a bad programming practice.  A 
dictionary is the cleanest way to hold a set of names/values that is not fixed. 
 Yes, you'll have to write ``cols['foo']`` instead of ``foo``; OTOH, setting 
them will not require any ugly magic...
Note also that string formatting can use values from a dictionary with very 
conveniently: ``"... {foo} ...".format(**cols)``.

The next best thing if ``cols['foo']`` is too verbose for you is ``cols.foo``: 
create an object which will contain the values as instance variables (that's a 
good use for setattr()).
This is the most Pythonic solution if a dictionary doesn't suffice - it's what 
most object-relational mappers do.

The third idea is to (ab)use a class statement.  A class statement in Python 
creates a temporary namespace *during the class definition* (we'll not be 
defining any methods or using it object-oriented stuff).
And the nice part is that you can put a class statement anywhere, even inside a 
function:
    def f():
        cols = {'foo': 42}  # however you fetch them...
        class temp_namespace:
            locals().update(cols)
            print(foo / 6)     # prints 7.0
        assert 'foo' not in locals()  # no effect outside the class!
This works both in CPython 2 and 3.  I'm not 100% sure that being able to 
change locals() in a class is guaranteed in all other implementations.  (Note 
again that locals() of a *function* are not a real dictionary and you *can't* 
change them - as I said these are fixed when the function is defined.)

The fourth idea if you must have code that says just ``foo`` to access columns 
is to use the exec statement - you can pass it a dictionary that will serve as 
globals and/or locals.  An upside is that the code will be a string and can be 
dynamic as well.

(BTW, if the code is not dynamic, how come you don't know the names you're 
accessing?  If you do, you could just set ``foo = cols['foo']`` etc. for every 
variable you need - tedious but no magic needed.)

Lastly, as you discovered you can dynamically create global variables.  
(As Terry said, just use the dictionary returned by ``globals()``; no need for 
setattr).
But this is a very last resort (changing globals for a single function is 
ugly), and somewhat dangerous - e.g. consider what happens if a column names 
changes and overwrites a function name you had...

----------
nosy: +cben

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue10043>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to