3

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?

4 Answers 4

2

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

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

Comments

2

The specific reason is that function definitions are compiled, but class bodies are executed within the scope of the class namespace.

Identifiers are not looked up when code is compiled, only when the compiled code is executed, so a function A that calls as-yet-undefined function B is fine as long as function B is defined before function A is invoked.

Note that you could very easily do

class MySecondClass():
    def __init__(self):
        self.prop1 = 0.
        self.prop2 = MyFirstClass()

class MyFirstClass():
    def __init__(self):
        self.prop=3

This changes those properties to instance properties instead of class properties, but this works because the function bodies are just compiled, not executed.

Comments

1

I think what is going on is that in all cases, the Python file is read from top to bottom. For function definitions, this doesn't matter because the function calls are not actually instantiated during the definition.

However, when calling a class constructor 'MyFirstClass' to define a class level attribute of 'MySecondClass', an attribute which will be integral to the definition of the class itself, then necessarily 'MyFirstClass' must be defined, because it must be instantiated at the moment of definition of the class, as opposed to being instantiated later, e.g., during later call from a method.

Comments

1

It all comes down to at what point code gets executed.

If you have a class:

class Foo:
    bar = func()

The "body" of the class gets executed when the class is defined, which happens when the module is imported. So, in this example, func gets called when the class is defined (when the module is imported) - which means func must be defined at that point in time, too.

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.

That's different. If you have functions living in a module:

def foo():
    return bar() + 2

def bar():
    return 3

This is allowed. When this module is imported, all that happens is the definition of two functions. The call to bar() inside foo only happens when you call foo, which doesn't happen automatically. By the time the call to bar() happens inside foo, the function bar will have been defined, so there is no issue here. For the same reason, you can write something like this:

def foo():
    return some_function_that_doesnt_exist() + 2

And you won't know about the error until you call foo, not at the time at which it is imported.

1 Comment

As another example, if you call foo() right after you define it, you'll have essentially the same problem you have when defining a class, because bar() isn't defined yet. Since most modules define all their classes and functions before using any of them, this is rarely even noticed. The key (as you note) is that function and class definitions are executed in Python, unlike many other languages.

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.