Conceptual inquiry about __main__ in Python

2019-07-08 03:38发布

问题:

I am currently working with Python and have been confused over the fact that functions are listed in __main__. I have been looking over multiple python scripts to try to find a common theme as to what functions warrant a place in __main__, but to no avail. Here I have a sample of my own code. firstfunction and anotherfunction are the only two functions in my code.

def main(argv):

    firstinput=""
    secondinput=""

    if len(argv) < 3 or len(argv) > 3:
        print """"Please set to:
                metisfinal.main(metisfinal.py, firstinput, secondinput)""""
        return
    else:
        firstinput = argv[1]
        secondinput = argv[2]

    firstfunction(firstinput, dictionary)
    anotherfunction(list, secondinput)

if __name__ == "__main__":
    main(sys.argv)

(I think) I know that the arguments and __main__ call are correct, but firstfunction and anotherfunction always return errors (because their arguments are not globally defined). I'm positive that this is arising from a faulty understanding of __main__, because all the other examples I've looked at, basically set up __main__ in the same manner.

What constitutes listing a specific function in __main__? I have stumbled over some Python code that has upwards of 30 functions within it, but the programmer only listed 2 of those functions in __main__. Likewise, sometimes a code will have classes in the main argument, like this one (Project is earlier defined as an object class):

def main(argv):

    filename = ""
    outputfilename = ""

    p = Project(filename, outputfilename, subdomainNames) 
    p.generateICs()

if __name__ == "__main__":
    main(sys.argv)

Conceptually, I can't understand why all the functions aren't listed... don't all of them need to be run or is __main__ simply initializing something?

Am I looking at atypical code? What key concepts of __main__ am I missing? And once I do find what functions to put into __main__, is there a specific way to format them?

回答1:

It's not clear what you mean by "listed in __main__". __main__ is not an entity in the source file. Rather, it is the name of the module, if you execute it directly. When you do if __name__=="__main__", you are telling Python to execute the code in that block if and only if the code is being executed as the main module --- that is, if it is a program being run. The code in the if __name__=="__main__" block will not be run if the module is imported from another module.

Note that you do not "list" functions in that if block. Instead, you put regular program code in that block that you want to be run. Often this code just calls one function. Often people call that function main(). But there is no special relationship between __main__ and main. You can call the function anything you like:

def snicklefritz():
    # This function will be run when you run the program
    print "You ran the program!"

if __name__ == "__main__":
    snicklefritz()

Try running this program (say, by saving it as "snicklefritz.py" and then doing python snicklefritz.py from the command line). You'll see "You ran the program!" printed. If instead you create a separate file that does import snicklefritz, the message won't be printed.

Note that there's nothing about "listing functions". For example, look at this program:

print "This will always be printed!"

if __name__ == "__main__":
    print "This will only be printed if you run the file as a program!"

Here the if __name__=="__main__" block does not "list" any functions. It just contains actual code that is run when the file is run as a script. People usually don't do this, though, because it's more tidy to have the code in a separate function instead of just sitting there "exposed" outside of a function.

As for other functions, you can define whatever other functions you like in your module, to be used either within that module, or to be used by other modules that import your module. Typically most of the functions in a module won't be used inside the if __name__=="__main__" block, because they won't be part of the "main" function. Instead, they'll be other functions intended for use by other code. For example:

def otherFunc(x):
    # Return x squared
    return x**2

def snicklefritz():
    # This function will be run when you run the program
    print "You ran the program!"

if __name__ == "__main__":
    snicklefritz()

otherFunc is not used at all in the module. That's fine. It may be that someone will want to import your module and use otherFunc themselves. Not every function has to be used within the same module, let alone be called from the if __name__=="__main__" block.



回答2:

You are misunderstanding the __main__ idiom.

Consider the program below, saved in a file called sum.py:

def read_numbers():
    n1 = int(raw_input())
    n2 = int(raw_input())
    return n1, n2

def sum_numbers(i1, i2):
    return i1+i2

def print_sum(i1, i2, i3):
    print "%d + %d = %d" % (i1, i2, i3)

v1, v2 = read_numbers()
sum = sum_numbers(v1, v2)
print_sum(v1, v2, sum)

It has three functions - one reads two numbers from standard input, another sums them and the third one prints the operation. After defining the functions, I call them in such a way that we read two numbers and print its sum. Fairly easy. If I execute it, I get something like this:

$ python sum.py 
12
34
12 + 34 = 46

Now, suppose I need to write another program which will read only one number - the other number is actually given. Since I already have a sum_numbers() function and a print_sum() function, I can fell tempted to reuse the sum module, which is a good thing:

import sum
MY_FIXED_NUMBER=3
n = int(raw_input())
value = sum.sum_numbers(n, MY_FIXED_NUMBER)
print_sum(n, MY_FIXED_NUMBER, value)

Great! However, if I execute it, what I got? This:

$ python three_sum.py 
12
34
12 + 34 = 46
12
12 + 3 = 15

WAT?! The program asked me for two numbers, printed the sum of them and then asked for a third number, which is correctly summed to 3. I just wanted to be asked for the third number, and just to print the sum with 3! What happened?

It happened that, when I import a module (such as import sum), all code inside it is executed. So, my module has two parts, one which defines useful functions which can be used elsewhere (which I will call the definition part), and a part where it executes this function in a way to get a specific result, so I can use the module as a program (which I will call the execution part). The execution part is always executed.

Fortunately, Python has a trick to allow me to execute the execution part only when the module is not imported. If I import a Python file with import, the module will have a variable called __name__, whose name will be the original name of the module:

>>> import sum
12
34
12 + 34 = 46
>>> sum.__name__
'sum'

However, if I run the Python file as a script ($ python sum.py), the __name__ variable will be there, but with a different name. Suppose I add a line such as

print __name__

at the end of my sum.py. When I run it again, I got it:

$ python sum.py
12
34
12 + 34 = 46
__main__

On the other hand, if I run three_sum.py, the result of print __name__ is way different:

$ python three_sum.py 
12
34
12 + 34 = 46
sum
12
12 + 3 = 15

Yes, the value of the __name__ variable when running the file as a script is __main__.

So, how could this help me? This way: I will put the execution part of my module inside a if condition. If the name of the module is __main__, it is because the file is running as a script with $ python sum.py - in this case, I should execute the execution part of my module. So my sum.py module will be this way now:

def read_numbers():
    n1 = int(raw_input())
    n2 = int(raw_input())
    return n1, n2

def sum_numbers(i1, i2):
    return i1+i2

def print_sum(i1, i2, i3):
    print "%d + %d = %d" % (i1, i2, i3)

if __name__ == "__main__":
    v1, v2 = read_numbers()
    sum = sum_numbers(v1, v2)
    print_sum(v1, v2, sum)

If I run $ python sum.py, I get the same as before:

$ python sum.py
12
34
12 + 34 = 46

However, if I run three_sum.py, everything is different:

$ python three_sum.py 
12
12 + 3 = 15

Now this works as expected. It works this way because the name of the module in the first execution is __main__, so the commands under if __name__ == "__main__" are executed. In the second execution, however, the name of the module is sum, so the commands under the if are not executed.

Even if your file is not designed to be imported, it is still a good practice to put the execution part of your file under if __name__ == "__main__", so you file is easily adapted to become a module.