nested function scopes

N

Nils Grimsmo

hi,

i'm having some trouble nesting functions. consider the following:


def h():
x = 1
def g():
print x # ok, x is taken from h
g()

def f():
x = 1
def g():
print x # this is not ok
x = 2 # this implies that x is local to g
g()

h() # ok
f() # UnboundLocalError: local variable 'x' referenced before assignment


when i run this code i get:


1
Traceback (most recent call last):
File "test.py", line 15, in ?
f() # UnboundLocalError: local variable 'x' referenced before assignment
File "test.py", line 12, in f
g()
File "test.py", line 10, in g
print x # this is not ok
UnboundLocalError: local variable 'x' referenced before assignment


how do i declare that x belongs to the parent function, so that i can do
assignments to it? do i have to put it into a compound object?

it would be very handy to be able to do this if i have nested functions
that use a lot of varables. only passing the variables you assign to as
packed compound parameters is a bit ugly, since what subset of all
variables this is might change.

i cannot say i like the python scope rules yet. they probably are
practical, but they seem complicated an unstructured to me.

take for example


class C:
y = 0
def f(self):
print y


which does not work unless y is a global. i understand and agree too why
y is not taken from the instance (self), because of the way classes and
instances relate in python. what i do not understand, is why y is not
taken from C, but from global, when nothing is specified. in the
previous example, with the functions h() and g(), g() took x from h().
why should not f() take y from C?



klem fra nils
 
T

Terry Reedy

Nils Grimsmo said:
hi,

i'm having some trouble nesting functions. consider the following:


def h():
x = 1
def g():
print x # ok, x is taken from h
g()

def f():
x = 1
def g():
print x # this is not ok
x = 2 # this implies that x is local to g
g()

h() # ok
f() # UnboundLocalError: local variable 'x' referenced before assignment ....
how do i declare that x belongs to the parent function, so that i can do
assignments to it?

Currently you cannot. As you probably know, 'global' declares that x
belongs to the module so you can assign to as well read it. This fall,
there was an extended discussion on pydev (at least 30 posts, I am sure) on
the subject of extending the idea of global to allow assignment to
intermediate scopes. Would such be desireable? If so, what syntax? See
python.org for B. Cannon's summaries or link to archives. Current bottom
line: no change.
do i have to put it into a compound object?

If compound = mutable collection, yes. Your example revised:

def f():
x = [1]
def g():
print x[0]
x[0] = 2
g()

The ease of doing this and the ugliness or other problems with proposed
alternatives inhibits change.
it would be very handy to be able to do this if i have nested functions
that use a lot of varables. only passing the variables you assign to as
packed compound parameters is a bit ugly, since what subset of all
variables this is might change.

You do not have to 'pass' the compound since you have read access to mutate
it.
i cannot say i like the python scope rules yet. they probably are
practical, but they seem complicated an unstructured to me.

They were simpler before nested scopes were introduced ;-). You can program
without them if you wish.
take for example

class C:
y = 0
def f(self):
print y
which does not work unless y is a global. i understand and agree too why
y is not taken from the instance (self), because of the way classes and
instances relate in python. what i do not understand, is why y is not
taken from C, but from global, when nothing is specified. in the
previous example, with the functions h() and g(), g() took x from h().
why should not f() take y from C?

This is a different kettle of fish. Classes, though callable, are not
functions and methods are not nested functions. Neither class nor instance
methods can be called during class construction because neither the class
nor instances thereof even exist. In any case, there is already a mechanism
for accessing class attributes: C.y in this case. Use it.

Terry J. Reedy
 
S

Scott David Daniels

Nils said:
i'm having some trouble nesting functions. consider the following:

You understand the rules, I think. You just don't like them. The
appropriate koan is "explicit is better than implicit".

I'd use something like this:

class Struct(object):
pass
...
def outer():
shared = Struct()
shared.counter = 1
def inner():
print 'inner says:', shared.counter
shared.counter += 1
inner()
inner()
print 'outer says:', shared.counter
> it would be very handy to be able to do this if i have nested
> functions that use a lot of variables. only passing the variables
> you assign to as packed compound parameters is a bit ugly,
> since what subset of all variables this is might change.

Python's goal is to make code more easy to _read_, not _write_.
I find it easier to read the outer function when I know
what might change on me. As my example shows, you needn't
thread all calls with the list of values.

There is another python idea here you haven't yet caught:
You speak of variables, but python only has associations of
names with values. When you think in terms of names and
associations, you may find 'inner's ability to see what 'outer'
is talking about without being able to affect 'outer's
associations reasonable.
i cannot say i like the python scope rules yet. they probably are
practical, but they seem complicated an unstructured to me.
Give it time. They are simple when you twist your head just right,
then they will seem completely simple.
class C:
y = 0
def f(self):
print y

Again, "explicit is better than implicit".

Either:
class D(object):
y = "D's y"
def f(self):
print self.y
Or:
class E(object):
y = "E's y"
def f(self):
print E.y

behave as you wish. Bindings first look to the instance and
then the class. Python is quite dynamic. There is, by the way,
a difference between the two above. Ponder the following two
subclasses:

class F(D):
y = "F(D)'s y"

class G(E):
y = "G(E)'s y"

F().f() prints F(D)'s y
G().f() prints E's y

to work in the same was as the two above, but you'd be wrong.
In the previous example, with the functions h() and g(), g()
why should not f() take y from C?
but which class should it choose in my examples? The instance's
exact class? The class in which the method was defined? You
could mean either, and be without a way to specify the other.

Further, you could do this:
y = "global"
class C:
def f(self):
print y

o = C()
o.f()
C.y = "classs variable"
o.f()

Should the second call to o.f() print something different than
the first one?

Anyhow, this is really too long already. I'll just include the
Struct definition I actually use when coding. It uses too much
magic to be clear, but it prints nicely in interactive mode.

This in my "toys.py":

class Struct(object):
def __init__(self, **kw):
self.__dict__.update(kw)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__,
', '.join(['%s=%r' % name_val for
name_val in self.__dict__.items()]))

Then (when I want data types):
import toys
class Holder(toys.Struct): pass # just to give a name

And my example becomes:

def outer():
shared = Holder(counter=1, name='Fred')
def inner():
print 'inner says:', shared
shared.counter += 1
inner()
inner()
print 'outer says:', shared


-Scott David Daniels
(e-mail address removed)
 
E

Emile van Sebille

Terry Reedy:
Nils Grimsmo:
i'm having some trouble nesting functions. consider the following: [snip]
def f():
x = 1
def g():
print x # this is not ok
x = 2 # this implies that x is local to g [snip]
how do i declare that x belongs to the parent function, so that i can do
assignments to it?

Currently you cannot.

Why not use the old method of passing variables to nested functions:
def f()
x = 1
def g(x=x):
print x
x = 2

Emile van Sebille
(e-mail address removed)
 

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

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top