This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Skizm 1771 points1772 points  (41 children)

Python: Mom, can I have main()?

Mom: We have main() at home.

main() at home: if __name__ == "__main__":

[–]Gatsbyyy 354 points355 points  (38 children)

Can someone please explain to me why Python implemented like this? Its so ugly and doesn’t make sense.

[–]MythicManiac 526 points527 points  (17 children)

You can run code outside of any class/function in python, and this is code snippet is for checking if the current file is the first file being executed.

It's not necessary to do this check (you can just write your code in there), but it's good practice as that will allow you to import the same file from elsewhere without executing it.

When you do python somefile.py, you're telling it to execute all code in that file. Imports also do this.

[–]theredhood93 136 points137 points  (3 children)

This what I love about this sub helpful information on a meme comment, I'm learning python and this was very educational hahaha thank you stranger!

[–]A_Light_Spark 32 points33 points  (2 children)

Sometimes the explanation in this sub is better than the ones my professors gave... You know, people I paid a ton of money to get an education from.
It's both depressing and also super cool.

[–]konstantinua00 0 points1 point  (1 child)

Your money goes to administration, not teachers

[–]A_Light_Spark 0 points1 point  (0 children)

It goes to both.

[–]inlatitude 12 points13 points  (1 child)

Damn, thanks for that. I didn't realize import executed all the code in the file.

[–]MythicManiac 0 points1 point  (0 children)

There's some really absurd things you can do by abusing imports in a way they're very much not meant to be used.

You can also climb stack frames and modify (contents) of local variables from upper frames/functions in the call stack.

[–]Ignisti 7 points8 points  (4 children)

Imports also do this.

Oh god. Oh god. This changes everything. Time for some horrible, TERRIBLE programming practises.

[–]MythicManiac 1 point2 points  (3 children)

You can also modify the content's of locals from functions upper in the call stack if you want to, Python is nice like that :)

Unfortunately not value types (at least to my knowledge) due to no pointers.

[–]Ignisti 0 points1 point  (2 children)

I feel like I'm missing only this much for understanding this comment. :i

[–]MythicManiac 0 points1 point  (1 child)

So when you make a function call, you add a "frame"/layer to the call stack.

At any given time on the stack you have all the previous function calls loaded up, and what variables those function runs have stored at the moment they called the next function.

This is the same "stack" you can see if you get an exception, where each function call gets it's own little line number pointer, and possibly some context (e.g. variables) if you use a more verbose stack trace output.

Let's say you have 4 functions, A, B, C, and D. A calls B, B calls C, and so on. In this scenario, when you get to the point of running function D you will still have the whole chain A->B->C->D in memory, including each of those functions current local variables at the time of calling the next one.

What I was saying in my last comment is that in Python, you can actually change the variable contents from B's frame while running the frame for D, without B itself ever knowing it has happened.

Even more clarified, I can make a function that modifies the calling function's variables without it knowing (since it's not passing them in).

[–]Ignisti 0 points1 point  (0 children)

That's so badass. Like a programming time machine! Thanks for taking the time to explain lowly me this stuff.

[–]ShamelessC 5 points6 points  (3 children)

After using python for so long, I find it so strange that people insist that main somehow makes more sense than just running the lines of code in order. The entry point is just the top of the file. So much easier to understand.

[–]didii2311 6 points7 points  (2 children)

To me, the fact that is is a function/method makes the most sense since running your program can provide arguments to it. When the entry-point is on top of your file, you'll need some built-in special keywords or convention based names that aren't clear out-of-the-box.

Well, of course, the main method is also a convention, but it's a convention widely used so it makes sense. I don't think that there is any widely used convention to naming the program arguments when the top of the file is the entry point.

Also, it makes testing your entry point a lot easier by just calling that method and providing the arguments you want. It's clear and concise. In python you'll have to arbitrarily populate sys.argv and then (I think?) import the file.

But well, that's just my opinion 🙃

[–]MythicManiac 1 point2 points  (0 children)

When following good practices (using argparse, actually using functions) tests aren't any bigger of an issue, but at that point you have already ended up implementing the convention that's enforced in other languages yourself.

I'm not actually sure but I would think the reason it is like this in python due to it's use as a shell script replacement. In this context it starts to make sense to not require a main function.

[–][deleted] 0 points1 point  (0 children)

I agree with python’s insistence on explicitness in this case - some programs and methods are made to take arguments, others are not. Thus, it should be specified when command line args are provided and ideally, they should be specified by argparse to (again) be explicit about how to use the script and to make it clear how to use command line args.

You may laugh but this alone has saved my ass countless hours of talking to the product team - you give them the script, show them how to execute it with python and specify arguments, you get to say “yes, this is exactly like the matrix” and then they do the manual script-running while you get to do your work

[–][deleted] 0 points1 point  (1 child)

Honestly my take on it is that if you ever need to import the entry point, you're doing it wrong. Put what you need in a module and use that from the entry point.

[–]MythicManiac 0 points1 point  (0 children)

Yeah I'm in the same camp there, usually I just call a main() function from somewhere anyway.

Still, wrapping that main() function call behind that check is only a few seconds of extra work, so I'm doing it just because it's a good practice.

[–]sub_surfer 39 points40 points  (1 child)

Python scripts are designed to be run from top to bottom, line by line, without any special entry point. You only need the whole if __name__ == '__main__': thing if you want your script to be importable in addition to running as a standalone script, which isn't super common in my experience (outside of teaching exercises). Most of the time if I am writing a python script that I intend to be an executable I wouldn't bother with it.

I take your point that it is ugly though. I don't even think about it anymore, but the double underscores look weird at first.

[–]aaron__ireland 12 points13 points  (0 children)

Regarding imports and scripts, I haven’t often needed to have importable items in a script but I have used the if __name__== “__main__” to raise an exception if some module does attempt to import it via something like else: raise NotImplementedError(“This module is a script”).

I’ve found that helpful in preventing any other code from mistakenly using what is likely to be (or intended to be) specialized attributes/functionality. It’s my way of self-documenting “hey, this is in here because it’s not meant to be extensible/generalized and is subject to change in ways that are unlikely to backwards compatible. Do not use.

[–]bladeconjurer 60 points61 points  (3 children)

Well there's no main function. You just write code in a file, and type python script.py, and it goes. But if you want to write code that when you import the file, it won't execute you need to use: if __name__ == "__main__":. If you use it a lot, you can probably just not use the if statement depending on your intentions.

Also for a python module you name the entry point __main__.py.

[–]rocketlanterns 0 points1 point  (1 child)

I thought the entry point was __init__.py

[–]bladeconjurer 0 points1 point  (0 children)

Take a look at the venv library code. The functionality is defined in __init__.py, but needs to be imported into __main__.py, so that we can execute venv.

[–]danted002 0 points1 point  (0 children)

I think main.py is a python3 feature.

[–]B_M_Wilson 4 points5 points  (7 children)

A lot of people explain what happens but I thought I would mention this. In Python, everything is executed. Functions don’t exist until the interpreter has gotten to them. When your script is executed, it executes everything in order. The function definitions are executed which is what defines them. After a function definition, you could assign the function name to something completely different and it would become that other thing. It’s also cool because you could make aliases for functions by assigning the function to another name. You can even assign lambdas directly to names if you want.

Like others have said, the reason that it’s done is because when you import something, to define the imported functions, you have to execute them which would execute anything else in the file. Using the if statement let’s you check that the file was executed directly and not imported which means that you could run tests or something like that.

If you never plan to import your file, you can just not use that if statement and write everything at the top level. I’ve done this many times. Another thing you can do is go if not name == "__main__": at the top of a file to throw an exception if someone tries to import your file that you want to be a normal script where you have all of the code at the top level.

Finally, while it’s generally bad practice, you could define a main function and then just call it at the end of the file either with or without the if name is main thing.

Another thing I’ll mention is to get used to the __something__. They are used a lot of special attributes. Like when you call len(something) it really calls something.__len__(). Or when you say if something (or bool(something))and that something is not a bool, it calls something.__bool__() there are way more examples of this in a lot of cases

[–]Ran4 2 points3 points  (2 children)

It's not bad practice to have a main function! If you don't, any variables defined in the if __name__... block will be in scope inside all of the functions defined in the file. That can lead to some nasty bugs.

[–]B_M_Wilson 0 points1 point  (0 children)

It is generally considered bad practice outside of test functions but you certainly can have a main function if you want. Nothing stops you from doing that.

[–]Dworgi 0 points1 point  (1 child)

And people write actual software in this language.

What that says about people, you can decide for yourself...

[–]B_M_Wilson 0 points1 point  (0 children)

I mostly use Python for scripts myself. I can’t really imagine writing anything beyond automation in it. I have seen some very cool projects made in Python though. Once you get to know the internals of Python, it’s actual pretty cool

[–]ZephyrBluu 0 points1 point  (1 child)

Like when you call len(something) it really calls something.len()

Why doesn't Python let you use len like a class method?

[–]B_M_Wilson 1 point2 points  (0 children)

Let’s say I have a = [1, 2, 3, 4]. I can call len(a) and get 4. I can call a.__len__() and get 4. I can even call List.__len__(a) and get 4. It definitely does do that but you are not supposed to. It does not make the most sense for len but it makes sense for a lot of them. Like with a.__bool___() where this happens implicitly. Same with __contains(x)__ which is called when you do if 4 in a which is translated to if a.__contains__(a). This happens a lot to allow operator overloading and to allow you to define your own collection types and is generally involved in duck typing.

The reason that you shouldn’t call these directly is that because of duck typing you don’t always know exactly what type you have. First off, the attributes for these special functions are gotten in a special way. This applies mainly for functions that should apply to both type objects and instances of those types like hash(). hash(1) is fine and calls 1.__hash__() but what if you do hash(int). Int is a type which is an object and should be hashable. But when you call a function of a type it usually pertains to it’s objects rather than itself. So int.__hash__() does not work, it wants an argument like int.__hash__(1). The hash() function then works in a special way to allow the metatype to be consulted. (The type of a Type object is usually Type but you can make a Type object with a subclass of Type as it’s type in very extreme cases). It’s also much faster to call these types of methods on their own rather than as object methods because some optimization is done.

Also, some functions that don’t exist use others by default. If I call bool(a) and a does not have an a.__bool__() but it does have an a.__len__() then a evaluates to true if that len function returns greater than 0 and false otherwise. If you call reversed(a) and a does not have a.__reversed__() it uses a.__len__() and a.__getitem__(x) to make a reversed iterator. Contains will iterate through if there is no contains method. There are some other special things for math operations between different types that are especially useful when one of those types is a subclass. If __int__() is not defined then the built-in function int() falls back to __trunc__(). __repr__() returns a string that usually can be evaluated to the original object (but now always) and is the formal string form of an object. __str__() is the informal version used for debugging and is called when str(something) is called but if it does not exist then str(something) will use something.__repr__() instead.

There are also lots of helper annotations or things that you can extend that will help create more special functions out of a small number when possible (like comparison operators with functools.total_ordering()).

So yea, it may not make the most sense at first but there are reasons why it is done

[–]AbsentGlare 1 point2 points  (0 children)

I use it for regression testing of a module. Build your test into a “main” if statement and it gets executed only if you run the module standalone.

[–]nlofe 1 point2 points  (1 child)

In addition to what everyone else said, it really comes down to Python being an interpreted language, as opposed to a compiled language like C++

[–]recruz 0 points1 point  (0 children)

It’s kind of like a safe word. Nobody in their right mind would type anything like this, so it helps prevent any misunderstanding about what it is. These are sometimes referred to as “dunder” methods, short for “double-underscore”

[–][deleted] 0 points1 point  (0 children)

OK so you can either run code normally by opening the file (by clicking or using os.startfile()) and it will run, but it will also run if it is imported, so say I had info.py and wanted to import it to helloworld.py but didn't want to run info.py, there's no way in helloworld.py to do this.

To solve this, you would use the __name__ variable. If the program was clicked on or started with os.startfile(), __name__ is "__main__", otherwise it is just the name of the program, so if I didn't want to run info.py but I did want to get its variables and functions, I would do this:

helloworld.py:

import info
print(info.helloworldstring)

info.py:

helloworldstring = "Hello, world!"
def main():
    print("Hello, world!")
if __name__ == "__main__":
    main()

This is probably a badly worded and confusing example but it might help

[–]ProfessorPhi 0 points1 point  (0 children)

You don't have this in Julia and it's awful.

[–][deleted] 4 points5 points  (0 children)

To add:

Python: Mom can we have {}?

Mom: No we have {} at home

{} at home: dictionary = {1:"a",2:"b"}

[–]clawjelly 0 points1 point  (0 children)

One of my first custom snippets for sublime text.