Weird behavior with lexical scope

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • mrstevegross

    Weird behavior with lexical scope

    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

    #2
    Re: Weird behavior with lexical scope

    mrstevegross <mrstevegross@g mail.comwrites:
    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

    Comment

    • mrstevegross

      #3
      Re: Weird behavior with lexical scope

      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

      Comment

      • Hamish McKenzie

        #4
        python bug when subclassing list?

        I want to write a Vector class and it makes the most sense to just subclasslist. 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__(cl s, a)
        except:
        print 'broken'
        return list.__new__(cl s, 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.

        Comment

        • skip@pobox.com

          #5
          Re: Weird behavior with lexical scope

          >def __init__(self, Inner=Inner):
          SteveOk, the Inner=Inner trick works. What the heck does that do, anyway?
          SteveI'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

          Comment

          • Kirk Strauser

            #6
            Re: Weird behavior with lexical scope

            At 2008-11-06T16:57:39Z, mrstevegross <mrstevegross@g mail.comwrites:
            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

            Comment

            • saju.pillai@gmail.com

              #7
              Re: Weird behavior with lexical scope

              On Nov 6, 9:57 pm, mrstevegross <mrstevegr...@g mail.comwrote:
              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.
              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


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

              Comment

              • Arnaud Delobelle

                #8
                Re: python bug when subclassing list?

                Hamish McKenzie <hamish@valveso ftware.comwrite s:
                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__(cl s, a)
                except:
                print 'broken'
                return list.__new__(cl s, 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?
                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__(s elf, *args)
                .... except TypeError:
                .... list.__init__(s elf, args)
                ....
                >>Vector([1,2,3])
                [1, 2, 3]
                >>Vector(1,2, 3)
                [1, 2, 3]
                >>Vector(1)
                [1]
                >>Vector()
                []

                HTH

                --
                Arnaud

                Comment

                • Terry Reedy

                  #9
                  Re: Weird behavior with lexical scope

                  saju.pillai@gma il.com wrote:
                  On Nov 6, 9:57 pm, mrstevegross <mrstevegr...@g mail.comwrote:
                  >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 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

                  Comment

                  • Lawrence D'Oliveiro

                    #10
                    Re: Weird behavior with lexical scope

                    In message <mailman.3599.1 226010740.3487. python-list@python.org >, Terry
                    Reedy wrote:
                    saju.pillai@gma il.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?

                    Comment

                    Working...