25

I am trying to reference an inner class from another inner class. I have tried both :

class Foo(object):

  class A(object):
    pass

  class B(object):
    other = A

and

class Foo(object):

  class A(object):
    pass

  class B(object):
    other = Foo.A

with respective results:

Traceback (most recent call last):
  File "python", line 1, in <module>
  File "python", line 6, in Foo
  File "python", line 7, in B
NameError: name 'A' is not defined

and

Traceback (most recent call last):
  File "python", line 1, in <module>
  File "python", line 6, in Foo
  File "python", line 7, in B
NameError: name 'Foo' is not defined

Is this possible?

crunk1
  • 2,560
  • 1
  • 26
  • 32
  • 1
    Well clearly from your attempts the answer is no, but what are you trying to do here eventually? **What problem are you trying to solve?** – Burhan Khalid Feb 12 '17 at 08:12
  • 1
    Why do you have nested classes at all? They are very rarely useful in Python. – Daniel Roseman Feb 12 '17 at 09:01
  • Using classes is not always a good fit for the problem you are trying to solve. – Roland Smith Feb 12 '17 at 09:25
  • I think you could find answer in this question: http://stackoverflow.com/questions/1765677/python-nested-classes-scope. – gaoxinge Feb 12 '17 at 12:46
  • 5
    Part 1) I knew this would inevitably lead to "you're not using pythonic design." I'm aware of that, but that argument is largely subjective. My application has a few different models, e.g. User and Transaction, located in models/user.py and models/transaction.py, respectively. Both model classes have protorpc message classes, "Msg", e.g. User.Msg and Transaction.Msg. For the sake of import brevity and to maintain the namespace between the two message classes, I made them inner classes. – crunk1 Feb 13 '17 at 09:43
  • 1
    Part 2) So, instead of "import user" and using "user.User" and "user.Msg", I could do "from user import User" and use "User" and "User.Msg". The example with "Transaction" is longer. The problem is that I also had a "Transaction.Status" that "Transaction.Msg" needed to reference. – crunk1 Feb 13 '17 at 09:47

3 Answers3

16

This is not possible, since everything you define in a class becomes a valid member only in an instance of that class, unless you define a method with @staticmethod, but there is no such property for a class.

So, this won't work either:

class Foo(object):
    x = 10

    class A(object):
        pass

    class B(object):
        other = x

This will work, but it is not what you intended:

class Foo(object):
  x = 10

  class A(object):
    pass

  class B(object):
    def __init__(self):
        self.other = Foo.A

f = Foo()
print(f.B().other)

The output is:

<class '__main__.Foo.A'>

The reason this works is that the methods (in this case __init__) are evaluated when the object is created, while assignment before the __init__ are evaluated while the class is read and interpreted.

You can get about the same thing you want by simply define all the classes inside a module of their own. The importing the module, makes it an object whose fields are the classes you define in it.

Israel Unterman
  • 13,158
  • 4
  • 28
  • 35
  • 8
    Thanks for saving me from the "why would you do that in Python?" argument and helping me understand. – crunk1 Feb 13 '17 at 09:48
  • Yes, unfortunately, your solution will not work on my "class B." Class B has metaclass logic that processes the B class variables. And yes, I could reorganize the classes (they are in the same module anyway). I was trying to leverage the intrinsic namespacing on inner classes though. – crunk1 Feb 13 '17 at 10:24
  • You should probably clarify the statement: "since everything you define in a class becomes a valid member only in an instance of that class". Foo.x, Foo.A, Foo.B are attached to the class object, not the instance. You can ask for an id() of each of these class qualified members and see that they exist and they are the same for the instance qualified member access. – Steve Tarver Aug 23 '19 at 21:28
0

I don't think it's good object oriented practice, but you can set inner class attributes at the outer class scope. For instance.

class Class2:

    class Labels:
        c2l1 = 'label 1'
        c2l2 = 'label 2' 

    class Params:
        pass 
        # p1 = None
        # p2 = None
        # p3 = None

    Params.p1 = Labels.c2l2
    Params.p2 = 1234


print(Class2.Params.p1)
print(Class2.Params.p2)
# print(Class2.Params.p3)

label 2
1234

These are all class attributes, but instance attributes should work similarly.

-1

Another solution here is to turn Foo into a module and dedent the code.

Or One can do this by defining the B class by using the type function. This uses the Foo local scope.

class Foo(object):

  class A(object):
    pass

  B = type('B', (object,), {'other': A})


print(Foo.A)
print(Foo.B)
print(Foo.B.other)

Prints out:

<class '__main__.Foo.A'>
<class '__main__.B'>
<class '__main__.Foo.A'>
spacether
  • 2,136
  • 1
  • 21
  • 28