15 years ago the answer to this question was:
The general rule in Python is that a function should be defined before its usage, which does not necessarily mean it needs to be higher in the code.
This is the correct answer, it also explains why the
if __name__=="__main__":
solution works
But it's not working for me. I have a class that initializes some class variables, one of which is calculated by a helper function that uses the value of another class variable.
But this yields a NameError
regardless of whether the class or the function is defined first.
In both cases the first execution happens under __main__
after the entire rest of the file has been seen.
def calc() -> str:
return Foo.x + '42'
class Foo:
x = 'bar'
y = calc()
def __init__(self):
self.a = 'lunch'
if __name__ == '__main__':
f = Foo()
print(f.x, f.y, f.a)
Error 1:
File "C:\Users\David\PycharmProjects\jadn2\jadn\otest.py", line 4, in calc
return Foo.x + '42'
^^^
NameError: name 'Foo' is not defined
Error 2:
File "C:\Users\David\PycharmProjects\jadn2\jadn\otest.py", line 4, in Foo
y = calc()
^^^^
NameError: name 'calc' is not defined
(I created a wrapper function bar()
that calls calc()
as in the original question, but as expected permuting the order makes no difference.)
How can I get the circular definition to work, given that the order of execution is supposed to be what matters?
class
bodies are not like function bodies: they are executed right away when the class
statement itself is found in the code, and must finish executing correctly (i.e., the class body: all statements indented in the block following the class statement) so that the class gets created.
Conversely, function bodies (the code indented inside a def
block) are just compiled when encountered, and are just executed when the function itself is called.
In your case, the body of the Foo
class tries to execute the function calc
- so far so good - but the calc
function expects the Foo
class already exists in the module, which it doesn't.
Your problem must be something else - there is some code smell there I can't really catch. There are ways to pass the partially initialized namespace of Foo
as an argument to calc
so that it could use the x
attribute - but it feels strange (if you want to go that way, inside the class body itself x
is already defined, and you can just use it for statements in the same scope, so you can pass x
as an argument: y = calc(x)
, after x
is defined)
Without knowing more about your code, possibly the best approach for you there is to have things that need attributes in Foo
to run after its body is defined, and before it is used - you simply have to put those statements below the class body itself.
For the snippet you have there, this will work:
def calc() -> str:
return Foo.x + '42'
class Foo:
x = 'bar'
def __init__(self):
self.a = 'lunch'
# here, Foo is defined, albeit lacking `y`.
# create the class attribute `Foo.y`:
Foo.y = calc()
# code that will instantiate and use Foo follows:
if __name__ == '__main__':
f = Foo()
print(f.x, f.y, f.a)
Otherwise, not that if you set y
inside a Foo method not in its body, it will also work seamlessly - method bodies are function bodies, and only run when they are called (at which point Foo
already exists)
def calc() -> str:
return Foo.x + '42'
class Foo:
x = 'bar'
def __init__(self):
self.y = calc()
self.a = 'lunch'
And you can also set y
as a class attribute and call calc
only once and still have it inside __init__
:
class Foo:
x = 'bar'
def __init__(self):
cls = type(self)
if not hasattr(cls, "y"):
cls.y = calc()
self.a = 'lunch'