1

When I execute

x = 0

def f():
    print('x' in locals())
    x = 1
    print('x' in locals())

f()

I get what I expect, namely

False
True

Yet when I execute

x = 3

def f():
    print(x, 'x' in locals())
    x = 7
    print(x, 'x' in locals())

f()

I expect to get

3 False
7 True

but instead I get the UnboundLocalError.

If Python knows that on the next line there comes an assignment of the label x in the local scope (and so the name x is already in the local scope but it wasn't assigned yet) then why does it let me inquire about x in my first code?

Added:

Why does it raise the error even though x = 7 comes after the first print(x, 'x' in locals())?

Leo
  • 213
  • 1
  • 6
  • Just FYI here, but don't use `locals()` in actual code. Or `globals()`. Or `eval()`. Or `exec()`. – TigerhawkT3 Jul 27 '16 at 15:01
  • 1
    The first searches for the character `"x"` in `locals()`. The second function tries to use the variable `x` and then assign to it. If you want to use the global version, specify `global x`. See http://stackoverflow.com/questions/370357/python-variable-scope-error – cdarke Jul 27 '16 at 15:03
  • @TigerhawkT3: thanks. I'm just using it while trying to learn. – Leo Jul 27 '16 at 15:03
  • @cdarke: and why does it raise the error even though `x = 7` comes after the first `print(x, 'x' in locals())`? – Leo Jul 27 '16 at 15:06
  • Because there is a compile phase and a run-phase. Python is not interpreted in the same way as a shell script. – cdarke Jul 27 '16 at 15:09

2 Answers2

2

dis.dis for first function:

  2           0 LOAD_CONST               1 ('x')
              3 LOAD_GLOBAL              0 (locals)
              6 CALL_FUNCTION            0
              9 COMPARE_OP               6 (in)
             12 PRINT_ITEM
             13 PRINT_NEWLINE

  3          14 LOAD_CONST               2 (1)
             17 STORE_FAST               0 (x)

  4          20 LOAD_CONST               1 ('x')
             23 LOAD_GLOBAL              0 (locals)
             26 CALL_FUNCTION            0
             29 COMPARE_OP               6 (in)
             32 PRINT_ITEM
             33 PRINT_NEWLINE
             34 LOAD_CONST               0 (None)
             37 RETURN_VALUE

dis.dis for second function:

  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 ('x')
              6 LOAD_GLOBAL              0 (locals)
              9 CALL_FUNCTION            0
             12 COMPARE_OP               6 (in)
             15 BUILD_TUPLE              2
             18 PRINT_ITEM
             19 PRINT_NEWLINE

  3          20 LOAD_CONST               2 (7)
             23 STORE_FAST               0 (x)

  4          26 LOAD_FAST                0 (x)
             29 LOAD_CONST               1 ('x')
             32 LOAD_GLOBAL              0 (locals)
             35 CALL_FUNCTION            0
             38 COMPARE_OP               6 (in)
             41 BUILD_TUPLE              2
             44 PRINT_ITEM
             45 PRINT_NEWLINE
             46 LOAD_CONST               0 (None)
             49 RETURN_VALUE

Critical difference is line: 4 26 LOAD_FAST 0 (x).

Basically, there is a assignment statement to name x in current scope, and therefore x is resolved as a local name. Resolving is performed in compilation phase. Compiled bytecode uses LOAD_FAST and not LOAD_GLOBAL.

Compilation to bytecode and execution are two independent steps - language is not actually interpreted line-by-line.

Community
  • 1
  • 1
Łukasz Rogalski
  • 22,092
  • 8
  • 59
  • 93
0

The error is from printing x, not from the call 'x' in locals(). You are not actually doing anything with the variable x when you do 'x' in locals() since you are giving it a string value, not a reference to a variable.