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

N

NevilleDNZ

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.$
 
S

Steve Holden

NevilleDNZ said:
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
 
N

NevilleDNZ

Steve said:
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
 
S

Steve Holden

NevilleDNZ said:
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
 
N

NevilleDNZ

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
 
S

Steve Holden

NevilleDNZ said:
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
 
N

NevilleDNZ

Steve said:
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
 
S

Steven D'Aprano

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.
 
S

Steven D'Aprano

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.
 
N

NevilleDNZ

Steven said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top