In Python, posso chiamare main () di un modulo importato?


87

In Python ho un modulo myModule.py in cui definisco alcune funzioni e un main () , che accetta alcuni argomenti della riga di comando.

Di solito chiamo this main () da uno script bash. Ora, vorrei mettere tutto in un piccolo pacchetto , quindi ho pensato che forse avrei potuto trasformare il mio semplice script bash in uno script Python e inserirlo nel pacchetto.

Quindi, come posso effettivamente chiamare la funzione main () di myModule.py dalla funzione main () di MyFormerBashScript.py? Posso anche farlo? Come posso passare gli argomenti ad esso?


Se hai importato myModule, dovresti essere in grado di chiamare myModule.main(). Cosa hai provato fino ad ora?
Marius

Sono preoccupato per gli argomenti di input, che di solito passo da uno script di shell.
Ricky Robinson

Ha senso chiamarlo con il subprocessmodulo?
BenDundee

Immagino che sarebbe più facile, sì.
Ricky Robinson

Risposte:


116

È solo una funzione. Importalo e chiamalo:

import myModule

myModule.main()

Se devi analizzare gli argomenti, hai due opzioni:

  • Analizzali main(), ma passa sys.argvcome parametro (tutto il codice di seguito nello stesso modulo myModule):

    def main(args):
        # parse arguments using optparse or argparse or what have you
    
    if __name__ == '__main__':
        import sys
        main(sys.argv[1:])
    

    Ora puoi importare e chiamare myModule.main(['arg1', 'arg2', 'arg3'])da un altro un altro modulo.

  • Avere main()accettare parametri che sono già analizzati (ancora una volta tutto il codice nel myModulemodulo):

    def main(foo, bar, baz='spam'):
        # run with already parsed arguments
    
    if __name__ == '__main__':
        import sys
        # parse sys.argv[1:] using optparse or argparse or what have you
        main(foovalue, barvalue, **dictofoptions)
    

    e importare e chiamare myModule.main(foovalue, barvalue, baz='ham')altrove e passare gli argomenti python secondo necessità.

Il trucco qui è rilevare quando il modulo viene utilizzato come script; quando esegui un file python come script principale ( python filename.py) non importviene utilizzata alcuna istruzione, quindi python chiama quel modulo "__main__". Ma se lo stesso filename.pycodice viene trattato come module ( import filename), allora python lo usa come nome del modulo. In entrambi i casi la variabile __name__è impostata e il test rispetto a quella ti dice come è stato eseguito il tuo codice.


3
Sicuro. Ma per quanto riguarda gli argomenti di input? Uso argparse, quindi quando chiamo lo script da un terminale, lo faccio $ python myModule -a input_a -b input_b --parameterC input_c. Come funzionerebbe dall'interno del codice Python? Questo è quello che non sono riuscito a trovare da una semplice ricerca.
Ricky Robinson

@ RickyRobinson: ampliato per mostrare che puoi averlo in entrambi i modi; basta passare gli argomenti analizzati o da analizzare.
Martijn Pieters

1
Grazie. Potresti anche specificare quale estratto di codice appartiene a quale modulo o script? Sembra molto più ingombrante di quello che pensavo all'inizio.
Ricky Robinson

@RickyRobinson: Entrambi gli estratti appartengono allo stesso modulo; L'ho reso esplicito.
Martijn Pieters

42

La risposta di Martijen ha senso, ma mancava qualcosa di cruciale che potrebbe sembrare ovvio agli altri ma che per me era difficile da capire.

Nella versione in cui usi argparse, devi avere questa riga nel corpo principale.

args = parser.parse_args(args)

Normalmente, quando si utilizza argparse solo in uno script, è sufficiente scrivere

args = parser.parse_args()

e parse_args trova gli argomenti dalla riga di comando. Ma in questo caso la funzione main non ha accesso agli argomenti della riga di comando, quindi devi dire ad argparse quali sono gli argomenti.

Ecco un esempio

import argparse
import sys

def x(x_center, y_center):
    print "X center:", x_center
    print "Y center:", y_center

def main(args):
    parser = argparse.ArgumentParser(description="Do something.")
    parser.add_argument("-x", "--xcenter", type=float, default= 2, required=False)
    parser.add_argument("-y", "--ycenter", type=float, default= 4, required=False)
    args = parser.parse_args(args)
    x(args.xcenter, args.ycenter)

if __name__ == '__main__':
    main(sys.argv[1:])

Supponendo che tu abbia chiamato questo mytest.py Per eseguirlo puoi fare uno di questi dalla riga di comando

python ./mytest.py -x 8
python ./mytest.py -x 8 -y 2
python ./mytest.py 

che ritorna rispettivamente

X center: 8.0
Y center: 4

o

X center: 8.0
Y center: 2.0

o

X center: 2
Y center: 4

O se vuoi eseguire da un altro script Python puoi farlo

import mytest
mytest.main(["-x","7","-y","6"]) 

che ritorna

X center: 7.0
Y center: 6.0

4
Questo è esattamente ciò di cui avevo bisogno - grazie mille per l'utile addendum
HFBrowning

Potrebbe aver funzionato con Python 2, ma in Python 3 non funziona più, non è possibile chiamare la funzione main () di un modulo:AttributeError: module 'sqlacodegen' has no attribute 'main'
NaturalBornCamper

26

Dipende. Se il codice principale è protetto da un ifcome in:

if __name__ == '__main__':
    ...main code...

allora no, non puoi fare in modo che Python lo esegua perché non puoi influenzare la variabile automatica __name__.

Ma quando tutto il codice è in una funzione, allora potrebbe essere in grado di farlo. Provare

import myModule

myModule.main()

Funziona anche quando il modulo si protegge con un file __all__.

from myModule import *potrebbe non essere mainvisibile a te, quindi devi davvero importare il modulo stesso.


Oh ok, grazie per il chiarimento. Ho messo tutto in una funzione main (), quindi dovrebbe essere ok. Sono più preoccupato di come passare gli argomenti di input a questo "secondo" main. Qualche modo semplice per farlo?
Ricky Robinson

Certo:import sys; module.main(sys.argv);
Aaron Digulla

Questa è un'ottima spiegazione alternativa sull'accesso __main__che mi ha aiutato, grazie @AaronDigulla
Charlie G

Ecco un trucco per chiamare una principale da un'altra funzione: stackoverflow.com/a/20158605/3244382
PatriceG

3

Ho avuto la stessa esigenza di utilizzo argparseanche. Il problema è che la parse_argsfunzione di argparse.ArgumentParserun'istanza di un oggetto prende implicitamente i suoi argomenti per impostazione predefinita da sys.args. Il lavoro, seguendo la linea di Martijn, consiste nel renderlo esplicito, in modo da poter cambiare gli argomenti a cui si passa parse_argscome desiderato.

def main(args):
    # some stuff
    parser = argparse.ArgumentParser()
    # some other stuff
    parsed_args = parser.parse_args(args)
    # more stuff with the args

if __name__ == '__main__':
    import sys
    main(sys.argv[1:])

Il punto chiave è passare gli argomenti alla parse_argsfunzione. Più tardi, per usare il principale, fai come dice Martijn.


3

La risposta che stavo cercando è stata risolta qui: Come usare python argparse con args diversi da sys.argv?

Se main.pye parse_args()è scritto in questo modo, l'analisi può essere eseguita correttamente

# main.py
import argparse
def parse_args():
    parser = argparse.ArgumentParser(description="")
    parser.add_argument('--input', default='my_input.txt')
    return parser

def main(args):
    print(args.input)

if __name__ == "__main__":
    parser = parse_args()
    args = parser.parse_args()
    main(args)

Quindi puoi chiamare main()e analizzare argomenti con parser.parse_args(['--input', 'foobar.txt'])esso in un altro script python:

# temp.py
from main import main, parse_args
parser = parse_args()
args = parser.parse_args([]) # note the square bracket
# to overwrite default, use parser.parse_args(['--input', 'foobar.txt'])
print(args) # Namespace(input='my_input.txt')
main(args)

0

Supponendo che tu stia tentando di passare anche gli argomenti della riga di comando.

import sys
import myModule


def main():
    # this will just pass all of the system arguments as is
    myModule.main(*sys.argv)

    # all the argv but the script name
    myModule.main(*sys.argv[1:])

Grazie. In realtà sto usando argparseinvece di sys.argv. Come cambierebbe in questo caso? Inoltre, dallo script esterno voglio solo passare alcuni argomenti di input che l'utente digita, mentre altri argomenti di input per lo script interno (myModule.py) sono hardcoded da me.
Ricky Robinson

Senza vedere il codice stesso è difficile rispondere alle specifiche. In generale, dovresti semplicemente passare qualsiasi argomento. Il *decomprime un array f(a) => f([1,2,3])vs f(*a) => f(1,2,3) potresti facilmente fare myModule.main(sys.argv[1], "other value", 39)o qualsiasi altra cosa.
agoebel
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.