3

Now the following code works perfectly with Python 3.7

class A:
    class B:
        def __init__(self):
            print("B")

    class C:
        def __init__(self):
            self.b = A.B()

def main():
    a = A.C()

if __name__ == "__main__":
    main()

It prints a B on the screen.

However, with a small modification that tries to introduce dataclass, the code cannot run well.

from dataclasses import dataclass

class A:
    class B:
        def __init__(self):
            print("B")

    @dataclass
    class C:
        b = A.B()

def main():
    a = A.C()

if __name__ == "__main__":
    main()

Python reports -- for b = A.B() -- NameError: name 'A' is not defined.

Does anyone know how to fix this issue to achieve the same result with dataclass? And why does it say name 'A' is not defined?

1
  • Just unnest your class, and if you really need that namespacing, just add A.C = C. Note, your dataclass is not equivalent to what you had before. Commented Oct 2, 2019 at 5:15

2 Answers 2

4

A class object is not created until the end of body of a class statement is reached, which is why your class A cannot be referenced while it is still being defined. Referencing A inside the __init__ method, on the other hand, is valid because the class A is already defined when the __init__ method is called.

You can instead use typing.TypeVar to define a forward-referencing type A.B for b, and assign to it a default value of a field with a default_factory function that returns an instance of A.B when called:

from dataclasses import dataclass, field
from typing import TypeVar

class A:
    class B:
        def __init__(self):
            print("B")

    @dataclass
    class C:
        b: TypeVar('A.B') = field(default_factory=lambda: A.B())

def main():
    a = A.C()

if __name__ == "__main__":
    main()

This outputs:

B
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the explanation. Can I ask for some clarification: What do you mean by a class is being defined? In both programs, C should be part of A. Why is it that calling __init__ method of C in the first program is thought to be done after A's definition, while specifying b = A.B() in C in the second program is thought to be done within A's definition?
class is an instruction in python. It's not a declaration like in other languages
Thanks! But frankly I am still (or maybe even more) confused. Then what's the difference between an instruction and a declaration in this context? I have background in C/C++. If you would like to draw any comparison I am more than happy to hear.
0

Another way to defer its initialisation until class A is defined and available, without touching the generated __init__:

@dataclass
class C:
    b: TypeVar('A.B') = field(init=False)

def __post_init__(self):
    self.b = A.B()

Please refer to the official docs

1 Comment

This is good unless you're defining C.b in the auto-generated __init__, i.e. C(b=A.B(foo)).

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.