Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Weird behavior with lexical scope

6 views
Skip to first unread message

mrstevegross

unread,
Nov 6, 2008, 11:57:39 AM11/6/08
to
I ran into a weird behavior with lexical scope in Python. I'm hoping
someone on this forum can explain it to me.

Here's the situation: I have an Outer class. In the Outer class, I
define a nested class 'Inner' with a simple constructor. Outer's
constructor creates an instance of Inner. The code looks like this:

=========
class Outer:
class Inner:
def __init__(self):
pass
def __init__ (self):
a = Inner()
Outer()
=========

However, the above code doesn't work. The creation of Inner() fails.
The error message looks like this:

File "/tmp/foo.py", line 12, in <module>
Outer()
File "/tmp/foo.py", line 10, in __init__
a = Inner()
NameError: global name 'Inner' is not defined

This surprises me! Since the construction of Inner takes place within
the lexical scope 'Outer', I assumed the interpreter would search the
Outer scope and find the 'Inner' symbol. But it doesn't! If I change:
a = Inner()
to
a = Outer.Inner()

it works fine, though.

So, can anyone explain to me how Python looks up symbols? It doesn't
seem to be searching the scopes I expected...

Thanks,
--Steve

Arnaud Delobelle

unread,
Nov 6, 2008, 12:27:55 PM11/6/08
to
mrstevegross <mrstev...@gmail.com> writes:

> I ran into a weird behavior with lexical scope in Python. I'm hoping
> someone on this forum can explain it to me.
>
> Here's the situation: I have an Outer class. In the Outer class, I
> define a nested class 'Inner' with a simple constructor. Outer's
> constructor creates an instance of Inner. The code looks like this:
>
> =========
> class Outer:
> class Inner:
> def __init__(self):
> pass
> def __init__ (self):
> a = Inner()
> Outer()
> =========
>
> However, the above code doesn't work. The creation of Inner() fails.

This is because there isn't lexical scoping in class scopes.

Try replacing

a = Inner()

with

a = Outer.Inner()


Alternatively,

class Outer:
class Inner:
...
def __init__(self, Inner=Inner):
a = Inner()

HTH

--
Arnaud

mrstevegross

unread,
Nov 6, 2008, 12:51:17 PM11/6/08
to
> def __init__(self, Inner=Inner):

Ok, the Inner=Inner trick works. What the heck does that do, anyway?
I've never seen that formulation.

--Steve

Hamish McKenzie

unread,
Nov 6, 2008, 12:46:50 PM11/6/08
to pytho...@python.org
I want to write a Vector class and it makes the most sense to just subclass list. I also want to be able to instantiate a vector using either:

Vector( 1, 2, 3 )
OR
Vector( [1, 2, 3] )


so I have this:

class Vector(list):
def __new__( cls, *a ):
try:
print a
return list.__new__(cls, a)
except:
print 'broken'
return list.__new__(cls, list(a))


doing Vector( 1, 2, 3 ) on this class results in a TypeError - which doesn't seem to get caught by the try block (ie "broken" never gets printed, and it never tries to

I can do pretty much the exact same code but inheriting from tuple instead of list and it works fine.

is this a python bug? or am I doing something wrong?

thanks,
-h.

sk...@pobox.com

unread,
Nov 6, 2008, 1:10:41 PM11/6/08
to mrstevegross, pytho...@python.org

>> def __init__(self, Inner=Inner):
Steve> Ok, the Inner=Inner trick works. What the heck does that do, anyway?
Steve> I've never seen that formulation.

Understanding that will put you on the path to scoping enlightenment.
Consider when that default assignment is established and how that might
differ from the assignment that occurs when __init__ is called.

Skip

Kirk Strauser

unread,
Nov 6, 2008, 1:50:57 PM11/6/08
to
At 2008-11-06T16:57:39Z, mrstevegross <mrstev...@gmail.com> writes:

> class Outer:
> class Inner:
> def __init__(self):
> pass
> def __init__ (self):
> a = Inner()
> Outer()

Try instead:

class Outer:
def __init__(self):
a = self.Inner()


--
Kirk Strauser
The Day Companies

saju....@gmail.com

unread,
Nov 6, 2008, 2:26:40 PM11/6/08
to

AFAIK, when 'Outer.__init__' executes, 'Inner' is first searched for
within 'Outer.__init__()'s local namespace. Since 'Inner' is defined
outside the function namespace, the search will fail. Python then
looks at the module level namespace - where Inner is again not defined
(only 'Outer' is available in the module namespace), the final search
will be in the interpreter global namespace which will fail too. When
you change your code from 'Inner' to 'Outer.Inner', the module level
namespace search will match ( or atleast that's how i think it should
all work :) )

Try this ..

class Outer:
def __init__(self):


class Inner:
def __init__(self): pass

a = Inner()
Outer()

This should work, because the Outer.__init__ namespace (first
namespace being searched) has Inner defined within it

-srp

Arnaud Delobelle

unread,
Nov 6, 2008, 3:05:52 PM11/6/08
to
Hamish McKenzie <ham...@valvesoftware.com> writes:

You're doing something wrong!

When you call Vector.__new__(cls, *a), this creates a new list object
(call it L) and then calls Vector.__init__(L, *a) automatically, falling
back to list.__init__(L, *a) (which won't work when a has more than one
element). In fact list initialisation is done in list.__init__, not in
list.__new__ (as opposed to tuple initialisation because tuples are
immutable).

A solution:

>>> class Vector(list):
... def __init__(self, *args):
... try:
... list.__init__(self, *args)
... except TypeError:
... list.__init__(self, args)
...
>>> Vector([1,2,3])
[1, 2, 3]
>>> Vector(1,2,3)
[1, 2, 3]
>>> Vector(1)
[1]
>>> Vector()
[]

HTH

--
Arnaud

Terry Reedy

unread,
Nov 6, 2008, 5:32:12 PM11/6/08
to pytho...@python.org

So do that.

> AFAIK, when 'Outer.__init__' executes, 'Inner' is first searched for
> within 'Outer.__init__()'s local namespace. Since 'Inner' is defined
> outside the function namespace, the search will fail. Python then
> looks at the module level namespace - where Inner is again not defined
> (only 'Outer' is available in the module namespace), the final search
> will be in the interpreter global namespace which will fail too. When

Interpreter global namespace == builtins

> you change your code from 'Inner' to 'Outer.Inner', the module level
> namespace search will match ( or atleast that's how i think it should
> all work :) )

Correct

> Try this ..

Why?

> class Outer:
> def __init__(self):
> class Inner:
> def __init__(self): pass
> a = Inner()

This create a duplicate Inner class object for every instance of Outer,
which is almost certainly not what the OP wants.

> Outer()
>
> This should work, because the Outer.__init__ namespace (first
> namespace being searched) has Inner defined within it

tjr

Lawrence D'Oliveiro

unread,
Nov 7, 2008, 4:22:47 PM11/7/08
to
In message <mailman.3599.1226010...@python.org>, Terry
Reedy wrote:

> saju....@gmail.com wrote:
>
>> class Outer:
>> def __init__(self):
>> class Inner:
>> def __init__(self): pass
>> a = Inner()
>
> This create a duplicate Inner class object for every instance of Outer,
> which is almost certainly not what the OP wants.

Does it matter?

0 new messages