proc A def/calls proc B: variable scoping rules.

Discussion in 'Python' started by NevilleDNZ, Aug 16, 2006.

  1. NevilleDNZ

    NevilleDNZ Guest

    Can anyone explain why "begin B: 123" prints, but 456 doesn't?

    $ /usr/bin/python2.3 x1x2.py
    begin A:
    Pre B: 123 456
    begin B: 123
    Traceback (most recent call last):
    File "x1x2.py", line 13, in ?
    A()
    File "x1x2.py", line 11, in A
    B()
    File "x1x2.py", line 7, in B
    print "begin B:",x1,x2
    UnboundLocalError: local variable 'x2' referenced before assignment


    $ cat x1x2.py
    #!/usr/bin/env python
    def A():
    print "begin A:"
    x1=123;
    x2=456;
    def B():
    print "begin B:",x1,x2
    x2 = x2 - 1; # comment out this line and script x1x2 magically
    works!!
    print "end B:",x1,x2
    print "Pre B:",x1,x2
    B()
    print "end A:",x1,x2
    A()

    $ /usr/bin/python2.3
    Python 2.3.4 (#1, Mar 10 2006, 06:12:09)
    [GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>>



    $ /usr/bin/python2.3 -V
    Python 2.3.4

    $ /usr/local/bin/python2.4 -V
    Python 2.4.2
    $ /usr/local/bin/python2.4 x1x2.py
    begin A:
    Pre B: 123 456
    begin B: 123
    Traceback (most recent call last):
    File "x1x2.py", line 13, in ?
    A()
    File "x1x2.py", line 11, in A
    B()
    File "x1x2.py", line 7, in B
    print "begin B:",x1,x2
    UnboundLocalError: local variable 'x2' referenced before assignment

    # I compiled up 2.4 from the FC4 source, but 2.3 was from SL4.3
    $ python -V
    Python 2.4.2
    $ python
    Python 2.4.2 (#1, Aug 15 2006, 21:51:33)
    [GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>>

    $
     
    NevilleDNZ, Aug 16, 2006
    #1
    1. Advertising

  2. NevilleDNZ

    Steve Holden Guest

    NevilleDNZ wrote:
    > Can anyone explain why "begin B: 123" prints, but 456 doesn't?
    >
    > $ /usr/bin/python2.3 x1x2.py
    > begin A:
    > Pre B: 123 456
    > begin B: 123
    > Traceback (most recent call last):
    > File "x1x2.py", line 13, in ?
    > A()
    > File "x1x2.py", line 11, in A
    > B()
    > File "x1x2.py", line 7, in B
    > print "begin B:",x1,x2
    > UnboundLocalError: local variable 'x2' referenced before assignment
    >
    >
    > $ cat x1x2.py
    > #!/usr/bin/env python
    > def A():
    > print "begin A:"
    > x1=123;
    > x2=456;
    > def B():
    > print "begin B:",x1,x2
    > x2 = x2 - 1; # comment out this line and script x1x2 magically
    > works!!


    Hardly surprising. This statement is an assignment to x2, which
    therefore becomes local to the function. Since no previous value has
    been assigned to this local, the exception occurs.

    regards
    Steve
    --
    Steve Holden +44 150 684 7255 +1 800 494 3119
    Holden Web LLC/Ltd http://www.holdenweb.com
    Skype: holdenweb http://holdenweb.blogspot.com
    Recent Ramblings http://del.icio.us/steve.holden
     
    Steve Holden, Aug 16, 2006
    #2
    1. Advertising

  3. NevilleDNZ

    NevilleDNZ Guest

    Steve Holden wrote:
    > Hardly surprising. This statement is an assignment to x2, which
    > therefore becomes local to the function. Since no previous value has
    > been assigned to this local, the exception occurs.


    But: In this case the assignment is never reached.... eg..
    #!/usr/bin/env python
    def A():
    print "begin A:"
    x1=123
    x2=456
    def B():
    print "begin B:",x1,x2
    if False: x2 = x2 + 210 # Magically disappears when this line is
    commented out.
    print "end B:",x1,x2
    print "pre B:",x1,x2
    B()
    print "end A:",x1,x2
    A()

    # same error message...
    $ ./x1x2.py
    begin A:
    Pre B: 123 456
    begin B: 123
    Traceback (most recent call last):
    File "./x1x2.py", line 13, in <module>
    A()
    File "./x1x2.py", line 11, in A
    B()
    File "./x1x2.py", line 7, in B
    print "begin B:",x1,x2
    UnboundLocalError: local variable 'x2' referenced before assignment

    I guess it is something to do with the scoping of duck typing.

    I WAS expecting that the A.x2 was visable until x2 is somehow (by
    assignment) made local. I guess I am learning that what you do to a
    variable in the middle of scope (even in an unreachable statement)
    effects the entire scope. Is there anyway to force x2 to be A.x2
    (without declaring it to be a global.x2)? Maybe I can put it into a
    A.local class...

    The result sh/could be:
    begin A:
    pre B: 123 456
    begin B: 123 456
    end B: 123 666
    end A: 123 666

    ThanX
    NevilleD
     
    NevilleDNZ, Aug 16, 2006
    #3
  4. NevilleDNZ

    Steve Holden Guest

    NevilleDNZ wrote:
    > Steve Holden wrote:
    >
    >>Hardly surprising. This statement is an assignment to x2, which
    >>therefore becomes local to the function. Since no previous value has
    >>been assigned to this local, the exception occurs.

    >
    >
    > But: In this case the assignment is never reached.... eg..


    Doesn't matter. It's not the *execution* of the assignment that makes
    the name local, it's the *existence* of the assignment, detected by
    static code analysis.

    regards
    Steve
    --
    Steve Holden +44 150 684 7255 +1 800 494 3119
    Holden Web LLC/Ltd http://www.holdenweb.com
    Skype: holdenweb http://holdenweb.blogspot.com
    Recent Ramblings http://del.icio.us/steve.holden
     
    Steve Holden, Aug 16, 2006
    #4
  5. NevilleDNZ

    NevilleDNZ Guest

    I inserted x1,x2 into A to force a wider scope and it works.

    #!/usr/bin/env python
    def A():
    print "begin A:"
    A.x1=123;
    A.x2=456;
    def B():
    print "begin B:",A.x1,A.x2
    A.x2 = A.x2 + 210; # problem gone.
    print "end B:",A.x1,A.x2
    print "pre B:",A.x1,A.x2
    B()
    print "end A:",A.x1,A.x2
    A()

    $ ./z1z2.py
    begin A:
    pre B: 123 456
    begin B: 123 456
    end B: 123 666
    end A: 123 666

    $ python -V
    Python 2.5b3

    I checked: This method even handles recursion giving a new instance of
    A.x2 each call.
    Is this the official way to scope/inherit scopes in sub procs?

    ThanX
    NevilleD
     
    NevilleDNZ, Aug 16, 2006
    #5
  6. NevilleDNZ

    Steve Holden Guest

    NevilleDNZ wrote:
    > I inserted x1,x2 into A to force a wider scope and it works.
    >
    > #!/usr/bin/env python
    > def A():
    > print "begin A:"
    > A.x1=123;
    > A.x2=456;
    > def B():
    > print "begin B:",A.x1,A.x2
    > A.x2 = A.x2 + 210; # problem gone.
    > print "end B:",A.x1,A.x2
    > print "pre B:",A.x1,A.x2
    > B()
    > print "end A:",A.x1,A.x2
    > A()
    >
    > $ ./z1z2.py
    > begin A:
    > pre B: 123 456
    > begin B: 123 456
    > end B: 123 666
    > end A: 123 666
    >
    > $ python -V
    > Python 2.5b3
    >
    > I checked: This method even handles recursion giving a new instance of
    > A.x2 each call.
    > Is this the official way to scope/inherit scopes in sub procs?
    >

    No. It's too horrible to contemplate without getting mild feelings of
    nausea. What exactly is it you are tring to achieve here (since I assume
    your goal wasn't to make me feel sick :)?

    regards
    Steve
    --
    Steve Holden +44 150 684 7255 +1 800 494 3119
    Holden Web LLC/Ltd http://www.holdenweb.com
    Skype: holdenweb http://holdenweb.blogspot.com
    Recent Ramblings http://del.icio.us/steve.holden
     
    Steve Holden, Aug 16, 2006
    #6
  7. NevilleDNZ

    NevilleDNZ Guest

    Steve Holden wrote:
    > No. It's too horrible to contemplate without getting mild feelings of
    > nausea. What exactly is it you are tring to achieve here (since I assume
    > your goal wasn't to make me feel sick :)?


    It is part of an algorithum:
    #!/usr/bin/env python
    def A(k, x1, x2, x3, x4, x5):
    def B():
    k = k - 1;
    B.out=A.out=A(k, B, x1, x2, x3, x4)
    return B.out
    if k <= 0: A.out = x4 + x5
    else: B()
    return A.out
    print A(10, 1, -1, -1, 1, 0);
    # correct output is -67

    The scope of k remains one problem, as it is passed as an argument to
    A.

    I think x1,x2,x3,x4 are meant to be lambdas as well... :)
    N
     
    NevilleDNZ, Aug 16, 2006
    #7
  8. On Tue, 15 Aug 2006 18:38:49 -0700, NevilleDNZ wrote:

    > UnboundLocalError: local variable 'x2' referenced before assignment
    >
    > I guess it is something to do with the scoping of duck typing.


    Er, no. Scoping and duck typing are completely different concepts.

    Scoping refers to the idea of where a variable name is valid.

    Duck typing refers to the concept that one should not explicitly test for
    the type of objects (the variable's content), but should rely on the
    object being able to handle the methods you call ("if it quacks like a
    duck, and swims like a duck, we can treat it as if it were a duck; if an
    object contains the same methods as a string, we should treat it as if it
    were a string").

    The two concepts are unrelated.

    You're error is precisely what the exception says: according to Python's
    scoping rules, x2 is a local variable, but you've tried to read the value
    of that name before setting it.

    > I WAS expecting that the A.x2 was visable until x2 is somehow (by
    > assignment) made local.


    You expected wrong.

    Here's some sample code that shows the scoping rules in action:


    # scope.py
    # globals
    a, b, c = "global a", "global b", "global c"

    def func0():
    print " ", a, b, c

    def func1():
    # define only a as a local variable
    a = "local a in func1"
    print " ", a, b, c


    def func2():
    # define both a and b as locals
    a = "local a in func2"
    b = "local b in func2"
    print " ", a, b, c

    def func3():
    # define a as a local, but in the wrong place
    try:
    print " ", a, b, c
    except UnboundLocalError:
    print "Attempt to access the value of a local " \
    "variable before setting it."
    a = "local a in func3"
    print " ", a, b, c

    def func4():
    # uses nested functions
    a = "local a in func4"
    print " ", a, b, c

    def func5():
    b = "local b in func5"
    print "Calling nested function func5:"
    print " ", a, b, c

    def func6():
    global a, b
    print "Calling nested function func6:"
    print " ", a, b, c

    func5()
    func6()

    for function in (func0, func1, func2, func3, func4):
    print "Calling function %s:" % function.__name__
    function()

    # end scope.py

    Basically, when you access a variable name on the left hand side of an
    assignment (e.g. "a = 1") ANYWHERE in a function, that name is local to
    that function UNLESS it has been declared global.

    When you access a variable name as the right hand side of an assignment,
    or as an expression (e.g. "print a"), Python searches for it following the
    scoping rules: first it searches for it in the function's local variables,
    then the local variables of the next higher scope, and so on, and finally
    it searches for it amongst the globals (which is the top-level scope of
    everything).

    Play around with the code and see if it makes sense.



    --
    Steven D'Aprano
     
    Steven D'Aprano, Aug 16, 2006
    #8
  9. On Tue, 15 Aug 2006 20:28:34 -0700, NevilleDNZ wrote:

    >
    > Steve Holden wrote:
    >> No. It's too horrible to contemplate without getting mild feelings of
    >> nausea. What exactly is it you are tring to achieve here (since I assume
    >> your goal wasn't to make me feel sick :)?

    >
    > It is part of an algorithum:


    Every piece of code is part of an algorithm. What is the algorithm
    supposed to accomplish, apart from giving people a headache?

    I'm not saying that what you've done can't ever be useful, but I'm with
    Steve on this one. Yuck.


    --
    Steven D'Aprano
     
    Steven D'Aprano, Aug 16, 2006
    #9
  10. NevilleDNZ

    NevilleDNZ Guest

    Steven D'Aprano wrote:
    > Basically, when you access a variable name on the left hand side of an
    > assignment (e.g. "a = 1") ANYWHERE in a function, that name is local to
    > that function UNLESS it has been declared global.

    ThanX Steven, I am still getting used to python scoping rules. I didn't
    realise that using a variable on the left affected the variables on the
    right. I WAS trying to avoid making the variable GLOBAL, and just pick
    it out of the superior proc's scope.
    >
    > When you access a variable name as the right hand side of an assignment,
    > or as an expression (e.g. "print a"), Python searches for it following the
    > scoping rules: first it searches for it in the function's local variables,
    > then the local variables of the next higher scope, and so on, and finally
    > it searches for it amongst the globals (which is the top-level scope of
    > everything).

    I am more used to nested scopes, as in pascal/C.
    >
    > Play around with the code and see if it makes sense.

    I will certainly dabble with your example further.

    Many ThanX
    NevilleD

    BTW: here is my poor attempt at porting the "man boy test" algorithum
    to python. As you can see in python I am still a boy... :)

    $ cat ./man_boy_test.py
    #!/usr/bin/env python
    def A(k, x1, x2, x3, x4, x5):
    A.k=k
    def B():
    A.k = A.k - 1
    B.out=A.out=A(A.k, B, x1, x2, x3, x4)
    return B.out
    if A.k <= 0: A.out = x4() + x5()
    else: B()
    return A.out
    if A(10,lambda:1,lambda:-1,lambda:-1,lambda:1,lambda:0)==-67:
    print "man"
    else:
    print "boy"
    # end man_boy_test.py

    $ ./man_boy_test.py
    boy
     
    NevilleDNZ, Aug 16, 2006
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jiong Feng
    Replies:
    0
    Views:
    845
    Jiong Feng
    Nov 19, 2003
  2. David Lozzi
    Replies:
    3
    Views:
    1,941
    David Lozzi
    Jun 1, 2005
  3. Replies:
    6
    Views:
    540
  4. Sean Ross
    Replies:
    3
    Views:
    128
    Aredridel
    Dec 25, 2003
  5. Replies:
    7
    Views:
    162
Loading...

Share This Page