3

It surprises me that this code gives the subsequent error (Python 2.7.3):

class Foo(object):
    class Bar(object):
        pass
    class Baz(object):
        class InnerBar(Bar):
            pass

results in:

Enthought Python Distribution -- www.enthought.com
Version: 7.3-2 (64-bit)

Python 2.7.3 |EPD 7.3-2 (64-bit)| (default, Apr 11 2012, 17:52:16)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
Type "credits", "demo" or "enthought" for more information.
Hello
>>> class Foo(object):
...     class Bar(object):
...         pass
...     class Baz(object):
...         class InnerBar(Bar):
...             pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in Foo
  File "<stdin>", line 5, in Baz
NameError: name 'Bar' is not defined

My suspicion is that this has to do with the particular execution of the equivalent Bar = type(...) class assignment, but even so, why doesn't Bar become available by closure within Baz?

Is there a way to workaround this for this precise kind of implementation (as in, I don't want to relax the constraint that Bar is a plain class defined inside of Foo, and that other classes within classes within Foo might want to inherit from Bar.

The particular use case that I have in mind is mimicking some database structures to make a more object oriented design that mirrors the schema in a data services project. Much of the code is autogenerated Python derived from a program that performs inspection on XSD, and so having classes defined within classes was a natural way to emulate certain things about XSD complex types, but with the added ability to also say when they are children of other complex types, etc. That's why I'm hesitant to pull a class outside of Foo for later inheritance.

0

2 Answers 2

3

There is no closure here. The body of a class is not a parent scope when nested code is executed.

As far as code in nested scopes is concerned, only the global and the local scopes exist, the Foo class body is not a namespace that can be accessed.

See the Naming and Binding documentation:

If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name. The scope of names defined in a class block is limited to the class block

Emphasis mine.

The work-around here is to use a nested function to produce a scope that can be accessed:

class Foo(object):
    def produce_nested_scope():
        class Bar(object):
            pass
        class Baz(object):
            class InnerBar(Bar):
                pass
        return Bar, Baz
    Bar, Baz = produce_nested_scope()
    del produce_nested_scope

Demo:

>>> class Foo(object):
...     def produce_nested_scope():
...         class Bar(object):
...             pass
...         class Baz(object):
...             class InnerBar(Bar):
...                 pass
...         return Bar, Baz
...     Bar, Baz = produce_nested_scope()
...     del produce_nested_scope
... 
>>> Foo.Bar
<class '__main__.Bar'>
>>> Foo.Baz
<class '__main__.Baz'>
Sign up to request clarification or add additional context in comments.

2 Comments

I'm still very unsure about why the documented process doesn't fully happen for Bar and Baz within the local namespace where the suite-execution is in progress for Foo. In whatever local namepsace is being developed for Foo before Foo is a bound name, within that namespace, why wouldn't all of Bar's suite complete, then Bar is bound in that namespace, then all of Baz's suite executes (including InnerBar which can look up one namespace level to find Bar since Bar would have been bound in that namespace), then Foo completes and is bound? What am I missing?
@EMS: Ah, I lack caffeine today. I thought you were using Foo inside Foo. I misread. No, class namespaces are not scopes for the purpose of looking up other names. Bar cannot be found because the Foo suite is not a parent scope.
1

Here's a horrible hack:

class Foo(object):
    def hack(dest):
        class Bar(object):
            pass
        class Baz(object):
            class InnerBar(Bar):
                pass

        dest.update(locals())
    hack(locals())
>>> Foo.Bar
<class '__main__.Bar'>
>>> Foo.Baz.InnerBar
<class '__main__.InnerBar'>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.