10

The main file, that intended to run everything, is now a mess of a dozen of variables (which are global by default in the top-level control flow), couple of structures and a middle-sized main loop. From other languages I've learned that globals are evil. I tend to put it all into a class in the same file and call just one method from a global control flow like following:

def MyClass:
  def __init__(self):
    self.value1 = 1
    ....

 if __name__ == "__main__":
   #inspect sys.argv here
   MyClass().main_proc()

Would you consider it a design plus? Is there a pythonic way to do this?

3
  • I'd suggest asking on programmers.stackexchange.com , but in general, I don't know any particular problem with that technique. Commented Apr 22, 2016 at 12:44
  • 2
    Python does not force you to use OOP like e.g. Java or C#, so you don't need to put stuff into classes if there is no real benefit from that for you. Especially creating a class instance just to group stuff is not the best way to go IMHO. You could extract that stuff into a module instead, this does not require any instances, it just needs to be imported. That way it's also in a separated file and namespace. Commented Apr 22, 2016 at 12:49
  • 1
    Also, the indentation of the if __name__ ... line is wrong, it must be at the outer level. Probably just a typo? Commented Apr 22, 2016 at 12:49

2 Answers 2

8

Python does not force you to use OOP like e.g. Java or C#, so you don't need to put stuff into classes if there is no real benefit from that for you.

Especially creating a class instance just to group stuff is not the best way to go IMHO. You could extract that stuff into a module instead. This does not require any instances, it just needs to be imported. That way it's also in a separated file and namespace.

Example:

main.py:

if __name__ == "__main__":
    import sys
    args = sys.argv[1:]
    if len(args) != 2:
        print("This script requires exactly two command-line arguments!")
        exit(1)

    import my_module
    exit_code = my_module.run(args) or 0
    exit(exit_code)
else:
    raise ImportError("Run this file directly, don't import it!")

my_module.py:

# initialization code to be run at import time goes here

def run(args):
    # do whatever you need to do
    print("Hello world!")
    print("You said <", args[0], "> and <", args[1], ">."

    # you may return an integer (1-255) as exit code if an error occurred,
    # else the default exit code is 0 (successful; no error)

However, don't take this approach as ultimate truth! It's my personal, (not so) humble opinion, but there are always some situations where one approach fits better, and some where others should be preferred.

Also this is mainly a design question and has no real impact on the stability or functionality of your programs. It may only improve the readability, but especially for small scripts it's not a good approach as it adds a lot of code that doesn't really do anything. It's only useful if you have a large project with several modules, I'd say.

For rather small scripts (single file or only very few modules) I would recommend to just define all classes and functions you need at the top and then use the standard if __name__ == "__main__" as entry point.

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

2 Comments

Thanks for your clarification. The concern is mainly not about grouping but about access. Your recommendation brings even more potential misuse, I mean the separate module may be eventually imported by somebody, which in the worst case may create some subtle but harmful interaction through globals. After C++ it feels like having module-wide variables, accessible to any module user is not safe. But probably I'm wrong and things differ here.
Well, there is no real access protection in Python at all. You only tell other programmers not to use a specific property by starting its name with a single underscore, e.g. _internal_variable. If you start the name with double underscore (except magic methods that also end with double underscore), the property becomes "private", but actually Python just performs name mangling and changes its name by adding the module before that. Also through the great introspection features of Python, you can get practically every property if you know what to look for.
-1

Creating a new project, I found and followed these tutorials:

Regarding the structuring guide, a lot of things like requirements.txt can be moved to pyproject.toml.

A section like this in pyproject.toml creates "main" entry points:

[project.scripts]
mycmd = "mypkg.mymod:run"

All three of mycmd mypkg mymod are the same in my case, but large projects will benefit from the (in my case) boiler plate.

Discussed here: Specifying command line scripts in pyproject.toml

From what I learned, this is how (new) Python projects should be structured and prepared for e.g. uploading to PyPI.

3 Comments

This doesn't answer the question.
This is about entry points. So I think people looking for this will profit from my answer. Also, it may very well be what OP needed when he opened the thread. Please reconsider the negative vote if that was you @GinoMempin.
The question was about a "main" function and how/where to organize variables, classes, and other data structures. Similar to how "old" OOP-like languages like Java have a "main" function. Your answer is about packaging scripts into a Python package and defining its entrypoint. It can be related (but so does a lot of other things related to Python-based projects). It's not directly answering the question at all.

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.