Python class scoping rules

后端 未结 2 2099
醉话见心
醉话见心 2020-12-13 09:17

EDIT: Looks like this is a very old \"bug\" or, actually, feature. See, e.g., this mail

I am trying to understand the Python scoping rules. More pr

2条回答
  •  情深已故
    2020-12-13 09:52

    First focus on the case of a closure -- a function within a function:

    x = "xtop"
    y = "ytop"
    def func():
        x = "xlocal"
        y = "ylocal"
        def inner():
     #       global y
            print(x)
            print(y)
            y='inner y'
            print(y)
        inner()  
    

    Note the commented out global in inner If you run this, it replicates the UnboundLocalError you got. Why?

    Run dis.dis on it:

    >>> import dis
    >>> dis.dis(func)
      6           0 LOAD_CONST               1 ('xlocal')
                  3 STORE_DEREF              0 (x)
    
      7           6 LOAD_CONST               2 ('ylocal')
                  9 STORE_FAST               0 (y)
    
      8          12 LOAD_CLOSURE             0 (x)
                 15 BUILD_TUPLE              1
                 18 LOAD_CONST               3 ()
                 21 LOAD_CONST               4 ('func..inner')
                 24 MAKE_CLOSURE             0
                 27 STORE_FAST               1 (inner)
    
     14          30 LOAD_FAST                1 (inner)
                 33 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
                 36 POP_TOP
                 37 LOAD_CONST               0 (None)
                 40 RETURN_VALUE
    

    Note the different access mode of x vs y inside of func. The use of y='inner y' inside of inner has created the UnboundLocalError

    Now uncomment global y inside of inner. Now you have unambiguously create y to be the top global version until resigned as y='inner y'

    With global uncommented, prints:

    xlocal
    ytop
    inner y
    

    You can get a more sensible result with:

    x = "xtop"
    y = "ytop"
    def func():
        global y, x
        print(x,y)
        x = "xlocal"
        y = "ylocal"
        def inner():
            global y
            print(x,y)
            y = 'inner y'
            print(x,y)
        inner()    
    

    Prints:

    xtop ytop
    xlocal ylocal
    xlocal inner y
    

    The analysis of the closure class is complicated by instance vs class variables and what / when a naked class (with no instance) is being executed.

    The bottom line is the same: If you reference a name outside the local namespace and then assign to the same name locally you get a surprising result.

    The 'fix' is the same: use the global keyword:

    x = "xtop"
    y = "ytop"
    def func():
        global x, y
        x = "xlocal"
        y = "ylocal"
        class Inner:
            print(x, y)
            y = 'Inner_y'
            print(x, y) 
    

    Prints:

    xlocal ylocal
    xlocal Inner_y
    

    You can read more about Python 3 scope rules in PEP 3104

提交回复
热议问题