This thread has been predicated on making things easier to learn. Tell me, at what point in a new Python programmer’s progress does it become necessary to make a single file that is both a script and a module? My personal expectation is that it would come alongside unit testing, which I would put WELL after most of the concepts that are necessary for understanding that one line, and certainly after the fundamental that Python modules are executable code rather than declarative. (For example, I would expect that potentially-failing imports, with consequent top-level try/catch, would come up sooner than unit testing does.)
Yes, there are good use-cases for this idiom, but they can wait. And if you really do need to impose this on brand new programmers, there’s really no benefit to one idiom or another - it’s just a mysterious line of text that you slap in there to make stuff happen, which is no worse than we find in so many other languages. There’s no reason to invent a new form of magic if all you’re going to do is say “copy/paste this exact thing, make sure you get it right”.
It’s only boilerplate if used in that way, and as has been mentioned, it is a massively overused idiom. I don’t think it’d feel as much like boilerplate if more scripts simply had top-level code.
But brevity on its own isn’t really much of a goal. We don’t have a statement import spam or ham that will attempt one import, and if it fails, import the other; instead, we use try: import spam except Import Error: import ham because that uses standard tools to achieve a goal. Python doesn’t need magic syntax to do things that can be done just as well with normal statements.
I use it quite often when prototyping or playing around with new ideas, mostly for purposes of debugging. So for my it’s quite a common thing to see; not in public projects, but in private ones.
The status quo is winning over any proposal thusfar.
If someone wanted to propose a new module attribute like __is_main__ which is semantically identical to __name__ == "__main__", I think that might have a chance. We could debate whether or not such a thing is really better. IMO that’s a new thread, for clarity, if anyone really wants to push for it. Be prepared for pushback.
I think it’s best to treat the decorator idea as rejected. No core devs are championing it, and it has several detractors in-thread. That’s enough signal for me.
I agree that the if __name__ == "__main__" boilerplate is somewhat intimidating/incomprehensible and hard to remember for a beginner. I can see why@entrypointis a potential improvement, but I think it loses one of the benefits of if __name__ == "__main__"which is that you can do things before calling main(), like fiddle with imports, and you can use the elsefor things too. I don’t think__is_main__is any less intimidating.
If we really want to make a change, what about a new builtin executing()which returns a boolean equivalent to __name__ == "__main__" so that you can do something like:
if executing():
import foo as bar
main()
else:
import baz as bar
which I could see as being easier to teach - essentially “you’re checking if this module is being executed, or just imported from somewhere else”.
That name isn’t accurate, since all modules are executed. You could try if is_main_module() but at that point it’s not really much less obscure than the status quo.
Fortunately, if you like this particular idea, you can do it without any change to the language. Just inject it into the builtins using sitecustomize, and it’ll be available.
The docs for __main__ are legible for a newbie imo. There’s going to be some learning when using Python for the first time. I learned about if __name__ == "__main__" before I learned about decorators, which are opaque compared to a variable like __name__. Give them something real rather than a hand-hold-y decorator.
As for modules, Python packaging has come a long way in the past few years to the point that pyproject.toml alone can be specified, with zero or one or more scripts mentioned in it like
which requires neither decorators nor __name__ used in the code, keeping the .py files clean and the packaging declarative, allowing multiple entry points, and no decorator implementations that execute immediately.
To be sure everything can be learned if one reads the docs, but one of the main attractions to Python as a newbie-friendly programming language is that most Python codes that beginners are likely to encounter read like pseudo code–code that one doesn’t need to consult a doc to get a reasonably good idea of.
So to me if the yardstick is “does this read like pseudo code?” then I’d say a @main-tagged[1] main function is closer to measuring up to that yardstick than a if __name__ == '__main__': guard.
I use the word deliberately because “decorator” as a terminology has to be learned, but something that intuitively looks like a tag does not ↩︎
Let us suppose we adopt a function or decorator that executes when a Python script is run. Calling it “main” or an “entry point” does not make much sense when you actually use it. The first question is why it does not run when the module is imported, since a module is also executed during import. Therefore, you would have to explain that it works only when the file is executed directly as a script. What is the point of that?
if __name__ == "__main__" already makes this clear. It may appear low-level, but it is indeed low-level, it operates at the interpreter level. A beginner has to learn it, and it is quite logical, as it is part of the module itself. In my opinion, it is one of the first concepts a beginner should learn.
Personally, I think that writing your own (importable) modules is a relatively advanced idea - beginners can go a long way just writing standalone scripts and using the stdlib and 3rd party modules. And given that you don’t need the if __name__ == '__main__' construct until after you have started writing your own modules, and you have found a need for modules that can be both imported and run standalone, I think it can be deferred for quite a long time.
We shouldn’t try to “fix” a feature that isn’t broken just because a lot of people use it unnecessarily.
is enough to have your own importables. I’d consider that rather basic (and it being so basic is one of the nice things about Python.)
Using if __name__ == '__main__' to write tests for that utils module is also something I’d consider a classic beginner step. Significantly simpler than learning to deal with pytest IMO.
Even for me as a somewhat experienced programmer, there would be advantages if I could write if __is_main__: instead of if __name__ == '__main__'. Autocompletion for one thing. But it’s already been stated that any such request should get it’s own thread.
I am happy to emphasize that current Python tutorials clearly explain this part in detail. From a teachability perspective, it seems that this is covered quite well.
Really? How many beginners are writing unit tests, and needing to invoke the script directly to do that (given how many test runners there are that don’t work that way)?
BTW, multiprocessing requires if __name__ == '__main__' to work properly (except when using the fork start method). Beginners need to learn this if they want to speed up their code with multiprocessing.
Beginners, in the sense in which I’m using the word, don’t use test runners.
I deliberately didn’t call them “unit tests”. The pattern for the very simplest tests is
if __name__=="__main__":
print(f(1,2)) # should be 3.0
or even
if __name__=="__main__":
df = pd.read_csv(file_path, engine=“python”, comment=“%”)
print(df.head(3))
As for how many beginners actually test the components of their code: not enough
Just looking at Why does this graph still show two outliers? as an example, from last week in the Python Help section, if this guy had tested the components of his code, he could have asked a better question.
That post incidentally also demonstrates the cargo-culting with if __name__=="__main__": main(). The code didn’t need it, and using the paradigm probably made it harder on the beginning python user to understand what python is doing, why and how.
It presumably also demonstrates that the same user would just cargo-cult any other way of doing the same thing. I don’t see why any other spelling would be more likely to mean anything more than “ensure the code runs” to that user.