3

Say I do this in python 3.6:

class A:
    class B:
        pass
    class C:
        x = B()

This fails complaining that B is not defined when instantiating it in C. However, this:

class A:
    x = 1
    y = x

    def f(self):
        return self

    z = f

works fine.

And of course, this:

class A:
    pass

class B:
    x = A()

also works.

Why do inner classes definitions not follow the same logical rules as everything else?

Charles Langlois
  • 4,198
  • 4
  • 16
  • 25
  • Also, [here](https://stackoverflow.com/questions/13905741/accessing-class-variables-from-a-list-comprehension-in-the-class-definition) is a related question, and I think understanding the answer can be illuminating. – juanpa.arrivillaga Dec 08 '17 at 18:02

1 Answers1

3

The problem here is not the definition order, which works like everything else, it is the special nature of class-body scope. Essentially, variables in the class-body scope can only be accessed using the class-namespace, e.g. MyClass.my_class_variable. The special aspect of it is that it does not create an enclosing scope, which is why you cannot access my_class_variable and are forced to use the class-namespace inside a method definition, for example. Read more about that in this answer. So again, B is in fact defined. Note, the following works:

In [4]: class A:
   ...:     class B:
   ...:         pass
   ...:     x = B.__name__
   ...:
   ...:

In [5]: A.x
Out[5]: 'B'

Now, you might hope the following would work:

In [6]: class A:
   ...:     class B:
   ...:         pass
   ...:     class C:
   ...:         x = A.B()
   ...:
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-a145c80eee84> in <module>()
----> 1 class A:
      2     class B:
      3         pass
      4     class C:
      5         x = A.B()

<ipython-input-1-a145c80eee84> in A()
      2     class B:
      3         pass
----> 4     class C:
      5         x = A.B()
      6

<ipython-input-1-a145c80eee84> in C()
      3         pass
      4     class C:
----> 5         x = A.B()
      6

However, it does not, because A hasn't actually been defined yet! So, the solution is something like this:

In [7]: class A:
   ...:     class B:
   ...:         pass
   ...:     class C:
   ...:         pass
   ...:
   ...:

In [8]: A.C.x = A.B

Edited

So, this example should be illuminating:

In [14]: x = 'global'

In [15]: class Foo:
    ...:     x = 'foooooo!'
    ...:     def bar(self):
    ...:         return x
    ...:

In [16]: Foo().bar()
Out[16]: 'global'

In [17]: Foo.x
Out[17]: 'foooooo!'
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • Thanks. I'm not sure I understand the rationale behind this behavior though. – Charles Langlois Dec 08 '17 at 18:43
  • 1
    @CharlesLanglois well, it's a simple way to implement class scope in a dynamic language. Fundamentally, you want your class to act analogously to the global namespace for instances of that class (and objects of types that inherit from that class), where you can bind state and behavior. It doesn't cause much problems, unless you try to do a lot of nesting in classes (which should be rare in Python - theres usually not a good reason to), or a lot of processing in the class body (again, probably not a good idea). – juanpa.arrivillaga Dec 08 '17 at 18:47
  • What do you mean by "act analogously to the global namespace"? – Charles Langlois Dec 09 '17 at 06:23
  • Why does class definition scope not behaving like global scope or function scope not matter? I find it confusing and frustrating that I can't define nested classes which refer to each other inside a class definition like I can in the global scope. As for not having good reasons to, I think that's a matter of taste. I think it makes lots of sense to package related things in a class body instead of poluting the module namespace. – Charles Langlois Dec 09 '17 at 06:30
  • Hope the following work : worked on python 2.7 ... odd – digitalsentinel Dec 09 '17 at 09:36