4

I am trying to use an if statement to check whether a variable has been assigned and is not None.

# Code that may or may not assign value to 'variable'

if variable: 
    do things

This throws an error at the line with the if statement: "UnboundLocalError: local variable 'variable' referenced before assignment".

I thought if the variable wasn't assigned it would just be interpreted as False?

I've tried if variable in locals(): to no avail.

What's going on? What can I do to achieve the result I'm looking for?

Thanks

6
  • 3
    if 'myvarname' in locals(): Commented Jan 25, 2018 at 23:29
  • Why would you want to check whether a variable has been assigned? This seems like a solution to something that shouldn't be a problem. Can you not just do variable = None before the code that may or may not change variable's value? Commented Jan 25, 2018 at 23:31
  • @Elazar "I've tried if variable in locals(): to no avail." Commented Jan 25, 2018 at 23:31
  • Flask Debugger Traceback Commented Jan 25, 2018 at 23:32
  • 1
    @roganjosh take a look at the traceback. Commented Jan 25, 2018 at 23:35

3 Answers 3

6

It's better to simply initialize x to None at the beginning and test for None (as in Ned's answer).


What's going on is that whenever you reference a variable on a "load" (i.e. rvalue), Python looks it up and requires it to exist.

If your variable is named x, the following throws:

if x in locals(): ...

And is not what you wanted, since it would have checked if the value that the variable holds is in locals(). Not whether the name x is there.

But you can do

if 'x' in locals(): ...
Sign up to request clarification or add additional context in comments.

3 Comments

It starts with a complicated explanation, then suggests an odd thing to do ('x' in locals()), then finally ends, almost as an afterthought, with the right thing to do.
The complicated explanation is the answer to the stated question: "I am trying to use an if statement to check whether a variable has been assigned (...) What's going on?"
True, but the real question is, "What can I do to achieve the result I'm looking for?"
4

At the beginning of the function initialize the variable to None. Then later you can check it with:

if x is not None:

1 Comment

This worked, thanks. "You can accept an answer in 4 minutes" --SO
0

Your assumption was wrong. There is no such thing as a uninitialized variable in Python. Any reference to a "variable" in Python either finds an entry in the namespace of some scope (Local, Enclosing, Global, or Builtin) or it's name lookup error.

It is better to think of Python statements of the form x=y as binding statements rather than assignments. A binding associates a name with an object. The object exists independently of its bindings (instantiated by a literal expression, for example). By contrast, most of the programming languages people are more familiar with have variables ... which have an associated type and location. Python "variables" are entries in a dictionary and the id() returns the the location of the object to which a name is bound. (The fact that this is a memory location is an implementation detail; but the principle is that Python "variable names" are not like the "variables" of more conventional languages).

Here's a bit more on that: https://softwareengineering.stackexchange.com/a/200123/11737

The best way to do accomplish this is simply refrain from using names before binding values thereto. Poking around in locals() or globals() for such things is just poor, ugly, coding practice.

10 Comments

I agree with the moral at the end, and it's important. However, the claim 'There is no such thing as a uninitialized "variable" in Python' is just a note about your own favorite terminology. It might be helpful for some people, but it's not "true" (or false).
It's intended to force the reader to rethink their model for how a late-binding language works. Technically Python doesn't "assign values to variables." It "binds values (objects) to names." The del operator in Python remove a name from the namespace. This might leave the object free for garbage collection. There's no way to "declare" a variable (associate it with a time and reserve space for it in the heap) in Python. That's now how late binding languages work.
There is no declaration in this sense, but there are declarations (even before 3.6 - a symbol in a load position) and there are variables, as a matter of definition. They work exactly like variables in java, most of the time, and the difference between "names" and "addresses" is mostly an implementation detail. If you believe your description helps the reader to avoid confusion, I cannot argue; it certainly helps explaining del and locals() etc. But it comes at the cost of losing abstraction, most of the time (in good code, that follows your advice) unnecessarily.
The model you suggest might confuse some readers if they encounter if False: x=5; else: print(x) inside a function, when there's a global x. I admit it will likely confuse beginners anyway.
@Elazar: it's separately important to understand the LEGB scoping rules; that there are a set of dictionary like namespaces searched by the interpreter for any name resolution: Local, Enclosing, Global, and Builtins. There are some additional complexities understanding nested scope and, in Python3, the nonlocal keyword for explicitly accessing the enclosing scope .
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.