pythonclassattributes

Why must classes be defined in order of appearance in python module?


Let's say I have a python file test.py that looks like this:

class MyFirstClass():
    prop=3

class MySecondClass():
    prop1 = 0.
    prop2 = MyFirstClass()

Then I can run this and it works:

from my_test import MyFirstClass, MySecondClass

obj = MySecondClass()

But if I write my python file like this, just inverting the definition order:

class MySecondClass():
    prop1 = 0.
    prop2 = MyFirstClass()

class MyFirstClass():
    prop=3

then I get an error saying that MyFirstClass is not defined.

So Python obviously expect MyFirstClass to be defined before it is used in MySecondClass.

But I'm just wondering why it is like that. After all, function definitions with def can be made in any order within a module. So I'm (perhaps naively) a bit surprised that class definitions must be made in order like this.

I suspect that this has something to do with the fact that I used MyFirstClass to initialize a class-level attribute of MySecondClass. But could someone clarify what is going on here?


Solution

  • You are on the right track with your thinking.
    The lines of code inside your function aren't executed when you define your function; they're executed when you call your function. The lines of code inside your class definition are executed when you define your class; they have to be, in order to shape the class you are writing.

    Here is a snippet showing operations on functions that are more analogous to class definition

    def my_function():
        pass
    
    my_function.some_attribute = 'foo'
    
    def another_function():
        pass
    
    # This line won't work if you tried to define my_function after it
    another_function.some_function = my_function;
    

    Here is some example code you can run to demonstrate this (tested on python 3.9)

    class MyClass:
        print('this code executes at module read time')
        my_attribute = 3
    
    print(f'I can access the MyClass namespace from this line; {MyClass.my_attribute=}')
    
    def my_function():
        print('this code executes when the function is called')
        this_will_raise_a_name_error_when_the_function_runs
    
    print('done with definitions')
    my_function()
    

    Functions need their own scope when they run to behave sensibly. Classes don't need their own scope when you define them to be defined sensibly. Further discussion of function scopes can be found here Short description of the scoping rules