0

Should all Python files in a package have the following code at the end? Is there a pep convention about this?

if __name__ == '__main__':
    pass

I don't want my code to run anything unless it's used in the way I intended for it - as a package, with the files being imported in.

I think I understand that the purpose of using this in some files is to separate what happens when the file is executed directly vs when it's imported and run.

EDIT: I've edited the title to better reflect what I realised I was trying to ask. The accepted answer still applies.

Milind Sharma
  • 152
  • 1
  • 2
  • 10
  • 3
    The code you posted is no protection at all. `pass` does literally nothing. – John Gordon Apr 15 '22 at 02:20
  • @JohnGordon I understand that. My thinking was that if the file is run directly, `pass` is simply an explicit way of ensuring that nothing except top level code is run. Strictly speaking, do I even need to add this line at all though since `pass` does nothing? Would a file without this be treated in the same way? – Milind Sharma Apr 15 '22 at 05:54
  • Potential duplicate: [What does if main do](https://stackoverflow.com/questions/419163/what-does-if-name-main-do)? – Liam Apr 15 '22 at 06:57
  • Does this answer your question? [What does if \_\_name\_\_ == "\_\_main\_\_": do?](https://stackoverflow.com/questions/419163/what-does-if-name-main-do) – Liam Apr 15 '22 at 11:59
  • @Liam I already knew what it did in the literal sense. It was more that I was unsure of the exact use case of the if __name__, and figured that it was best practice to add it even if there was no need, as explicit is better than implicit. In hindsight my question was vague as I wasn't sure exactly what I wanted to ask. – Milind Sharma Apr 15 '22 at 14:29
  • No worries @MilindSharma, sorry for making the bad assumption it was a duplicate, do you want me to delete my comment? – Liam Apr 15 '22 at 15:31
  • 2
    You should also be careful of asking opinion based questions as this was the reason your question got closed @MilindSharma – Liam Apr 15 '22 at 15:38

1 Answers1

3

The literal code as you have it no, as it's useless. pass is a keyword to "do nothing" and the interpreter will need to evaluate the condition first, so it's literally "check if running directly, then do nothing" which is almost equal to not having those two lines there at all and if excluding the condition check being evaluated it's equal to being a comment or not being present at all.

>>> import dis
>>> dis.dis("")  # no code to evaluate, just the default behavior
  1           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE

>>> dis.dis("pass")  # does nothing and/or is stripped away by the parser/compiler
  1           0 LOAD_CONST               0 (None)
              2 RETURN_VALUE

>>> dis.dis("""
... if __name__ == "__main__":
...     pass
... """)
  2           0 LOAD_NAME                0 (__name__)
              2 LOAD_CONST               0 ('__main__')
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE        8

  3     >>    8 LOAD_CONST               1 (None)
             10 RETURN_VALUE

If it's meant not in literal sense but a question of whether each of your modules should have a __main__ guard, it depends on your code/logic. In the end, even an import is a code (unlike #include in C/C++ which in simple terms copy-pastes the full file in its place) and it needs to run so the module is executed and ready for loading somewhere else.

The point of the guard is a very different thing. It prevents executing code which you or your users consider bad when executed such as trying to connect to a database in cases when there's none or when running a test suite, when using global variables which you want present conditionally or any other case you might want to have.

The guard, however, isn't supposed to prevent the normal Python behavior, that is, CPython interpreter reading the text file .py, parsing the text, compiling something in between, loading the scopes and perhaps making ready for using by some other module.

Making this might or might not make sense:

import sys

if __name__ == "__main__":
    # import and print only when executing, not when importing
    from mymodule.submodule import something
    print(something(), file=sys.stderr)

vs

import sys

# happens always
from mymodule.submodule import something
print(something(), file=sys.stderr)

Why the example with an import? Because an import or from ... import ... statement also executes a code. Let's say you have a print("Hello") in a submodule, then from mymodule.submodule import something will parse and load the submodule.py (or submodule/__init__.py / mymodule/submodule.py), execute all of its code such as imports, classes, defs to even create the objects for you to reference later on when importing them.

One can write a class with an undesired effect too:

class My:
    print("Hello")

and that will also be executed when something is imported from a module containing this code. It's also a context when class variables are evaluated, so trying to connect to a DB in that scope is also annoying to encounter, yet might have its use-case.

Therefore there's not a question of "should" but it's more of a politeness or correctness not to cause unexpected or annoying things for you, your test suite or your users whether it's an application or a library what you are creating.

And you can also introduce examples or tests at the end of a library's module too, which may also be considered a fair use of the __main__ guard yet a completely unnecessary step for some:

class Square:
    def __init__(self):
        print(f"I am a {self.__class__.__name__}")

    @property
    def is_shape(self):
        return True


if __name__ == "__main__":
    square = Square()
    assert square.is_shape

The __main__ guard can be found since CPython 1.0

A module can now find its own name by accessing the global variable __name__. Assigning to this variable essentially renames the module (it should also be stored under a different key in sys.modules). A neat hack follows from this: a module that wants to execute a main program when called as a script no longer needs to compare sys.argv[0]; it can simply do "if __name__ == '__main__': main()". (1994, source)

As for the related PEPs, it's a bit hard here as the original functionality is present since CPython 1.0.0 and the PEPs are only slightly relevant in the sense that they touch the topic of __name__ variable being present or somehow set:

Peter Badida
  • 11,310
  • 10
  • 44
  • 90
  • For the literal code that I wrote, would it be better to not have it there at all (I do not wish for my modules to be run as scripts)? I understand that the behaviour is exactly the same whether the guard is there or not, but I figured explicitly stating that the file should `pass` when run directly was better than letting it simply be implied. Is this a redundancy that I should avoid, or is it better to explicitly state it? – Milind Sharma Apr 15 '22 at 06:04
  • 1
    @MilindSharma Exactly, because the code does nothing and is just a noise in the file. A reader who is at least a bit experienced in Python would look there only to find `pass`. It'd only confuse and bring up questions whether the author missed something or what's the point of those lines. Imagine it being like a sticker on a glass pane saying "This is a glass pane." Doesn't make much sense, does it? Although I see your point and coming from the Zen of Python where explicit is better than implicit makes sense, in some cases too much explicitness causes just an unnecessary noise and redundancy. – Peter Badida Apr 15 '22 at 06:40
  • 1
    Great answer @PeterBadida, just wondering about the last href link it says more but when I click it, the query in github displays 0 search results, is that expected? – Liam Apr 15 '22 at 07:01
  • 1
    @Liam Nice catch. GitHub's search is broken yet in one more way! It needs a logged in user, unfortunately, otherwise it's even more broken than normally. :( – Peter Badida Apr 15 '22 at 07:04
  • 1
    Just tried it logged in, works perfectly now, thanks for the update, +1 for the great answer, keep up the good work – Liam Apr 15 '22 at 11:57