0

Take the following code sample

var = True
def func1():
    if var:
        print("True")
    else:
        print("False")
        # var = True

func1()

This prints True as one would expect.

However, if I uncomment # var = True, I get the error

UnboundLocalError: local variable 'var' referenced before assignment

Why does writing to a variable make an otherwise accessible variable inaccessible? What was the rationale behind this design choice?

Note I know how to solve it (with the global keyword). My question is why was it decided to behave this way.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Rufus
  • 5,111
  • 4
  • 28
  • 45
  • 3
    Does this answer your question? [Don't understand why UnboundLocalError occurs (closure)](https://stackoverflow.com/questions/9264763/dont-understand-why-unboundlocalerror-occurs-closure) – lorenzozane Nov 12 '20 at 10:17
  • I voted to reopen this question because it explicitly asks for the *language design reason* of this behaviour, not for the semantics/rules of the behaviour. The previous duplicates only answered the latter, not the former. – MisterMiyagi Nov 12 '20 at 13:30

4 Answers4

1

Because:

  • Namespaces exist: the same variable name can be used at module level and inside functions, and have nothing to do with each other.

  • Python does not require variables to be declared, for ease of use

  • There still needs to be a way to distinguish between local and global variables

  • In cases where there is likely unexpected behavior, it is better to throw an error than to silently accept it

So Python chose the rule "if a variable name is assigned to within a function, then that name refers to a local variable" (because if it's never assigned to, it clearly isn't local as it never gets a value).

Your code could have been interpreted as using the module-level variable first (in the if: line), and then using the local variable later for the assignment. But, that will very often not be the expected behavior. So Guido decided that Python would not work like that, and throw the error instead.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
0

This is described in section 4.2.2 Resolution of names

When a name is not found at all, a NameError exception is raised. If the current scope is a function scope, and the name refers to a local variable that has not yet been bound to a value at the point where the name is used, an UnboundLocalError exception is raised. UnboundLocalError is a subclass of NameError.

If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.

alex_noname
  • 26,459
  • 5
  • 69
  • 86
0

Python defaults to implicit variable declaration via assignment, in order to remove the need for additional explicit declarations. Just "implicit declaration" leaves several options what assignment in nested scopes means, most prominently:

  • Assignment always declares a variable in the inner-most scope.
  • Assignment always declares a variable in the outer-most scope.
  • Assignment declares a variable in the inner-most scope, unless declared in any outer scope.
  • Assignment declares a variable in the inner-most scope, readable only after assignment.

The latter two options mean that a variable does not have a scope well-defined just by the assignment itself. They are "declaration via assignment + X" which can lead to unintended interactions between unrelated code.

That leaves the decision of whether "writing to a variable" should preferably happen to isolated local or shared global variables.


The Python designers consider it more important to explicitly mark writing to a global variable.

Python FAQ: Why am I getting an UnboundLocalError when the variable has a value?

[...]
This explicit declaration is required in order to remind you that (...) you are actually modifying the value of the variable in the outer scope

This is an intentional asymmetry towards purely reading globals, which is considered proper practice.

Python FAQ: What are the rules for local and global variables in Python?

[...]
On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
0

If a variable name defined in the outer scope is used in a nested scope, it depends on what you do with it in this nested scope:

  1. If you only read a variable, it is the same variable.

  2. If you write to a variable, then Python automatically creates a new, local variable, different from the one in the outer scope.

    This local variable prevents access to a variable with the same name in the outer scope.

So writing to a variable don't change its scope, it creates a different, local variable.

You are not able to read this local variable before assigning to it.

MarianD
  • 13,096
  • 12
  • 42
  • 54