Czy w Pythonie mogę wywołać metodę main () zaimportowanego modułu?


86

W Pythonie mam moduł myModule.py, w którym definiuję kilka funkcji oraz funkcję main () , która pobiera kilka argumentów wiersza poleceń.

Zwykle nazywam to main () ze skryptu bash. Teraz chciałbym umieścić wszystko w małym pakiecie , więc pomyślałem, że może mógłbym zamienić mój prosty skrypt bash na skrypt Pythona i umieścić go w pakiecie.

Jak więc właściwie wywołać funkcję main () myModule.py z funkcji main () MyFormerBashScript.py? Czy mogę to zrobić? Jak przekazać do niego jakiekolwiek argumenty ?


Jeśli zaimportowałeś myModule, powinieneś być w stanie zadzwonić myModule.main(). Czego spróbowałeś do tej pory?
Marius,

Martwią mnie argumenty wejściowe, które zwykle przekazuję ze skryptu powłoki.
Ricky Robinson

Czy ma sens nazywanie tego z subprocessmodułem?
BenDundee,

Myślę, że byłoby łatwiej, tak.
Ricky Robinson

Odpowiedzi:


114

To tylko funkcja. Zaimportuj i nazwij to:

import myModule

myModule.main()

Jeśli potrzebujesz analizować argumenty, masz dwie możliwości:

  • Przetwórz je main(), ale przekaż sys.argvjako parametr (cały poniższy kod w tym samym module myModule):

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

    Teraz możesz importować i dzwonić myModule.main(['arg1', 'arg2', 'arg3'])z innego innego modułu.

  • Mają main()akceptować parametry, które są już przeanalizowane (ponownie cały kod w myModulemodule):

    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)
    

    importuj i wywołuj myModule.main(foovalue, barvalue, baz='ham')gdzie indziej i przekazuj argumenty Pythona w razie potrzeby.

Sztuczka polega na tym, aby wykryć, kiedy twój moduł jest używany jako skrypt; kiedy uruchamiasz plik pythona jako główny skrypt ( python filename.py), żadna importinstrukcja nie jest używana, więc python wywołuje ten moduł "__main__". Ale jeśli ten sam filename.pykod jest traktowany jako module ( import filename), to python używa go jako nazwy modułu. W obu przypadkach zmienna __name__jest ustawiona, a testowanie na jej podstawie powie Ci, jak Twój kod został uruchomiony.


3
Pewnie. Ale co z argumentami wejściowymi? Używam argparse, więc kiedy wywołuję skrypt z terminala, robię $ python myModule -a input_a -b input_b --parameterC input_c. Jak by to działało z poziomu kodu Pythona? Tego nie mogłem znaleźć w prostym wyszukiwaniu.
Ricky Robinson

@RickyRobinson: rozszerzone, aby pokazać, że możesz mieć to w obie strony; po prostu przekaż argumenty przeanalizowane lub do przeanalizowania.
Martijn Pieters

1
Dzięki. Czy mógłbyś również określić, który fragment kodu należy do którego modułu lub skryptu? Wygląda to o wiele bardziej uciążliwie niż myślałem na początku.
Ricky Robinson

@RickyRobinson: Oba fragmenty należą do tego samego modułu; Wyraźnie to wyraziłem.
Martijn Pieters

42

Odpowiedź Martijena ma sens, ale brakowało w niej czegoś kluczowego, co może wydawać się oczywiste dla innych, ale było dla mnie trudne do zrozumienia.

W wersji, w której używasz argparse, musisz mieć tę linię w głównej części.

args = parser.parse_args(args)

Zwykle, gdy używasz argparse tylko w skrypcie, po prostu piszesz

args = parser.parse_args()

i parse_args znajdź argumenty z wiersza poleceń. Ale w tym przypadku główna funkcja nie ma dostępu do argumentów wiersza poleceń, więc musisz powiedzieć argparse, jakie to argumenty.

Oto przykład

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:])

Zakładając, że nazwałeś ten mytest.py Aby go uruchomić, możesz wykonać dowolną z tych czynności z wiersza poleceń

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

który zwraca odpowiednio

X center: 8.0
Y center: 4

lub

X center: 8.0
Y center: 2.0

lub

X center: 2
Y center: 4

Lub jeśli chcesz uruchomić z innego skryptu Pythona, możesz to zrobić

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

który powraca

X center: 7.0
Y center: 6.0

4
Właśnie tego potrzebowałem - bardzo dziękuję za pomocny dodatek
HFBrowning

To mogło działać z Pythonem 2, ale w Pythonie 3 już nie działa, nie można wywołać funkcji main () modułu:AttributeError: module 'sqlacodegen' has no attribute 'main'
NaturalBornCamper

24

To zależy. Jeśli główny kod jest chroniony przez ifjak w:

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

to nie, nie możesz zmusić Pythona do wykonania tego, ponieważ nie możesz wpłynąć na zmienną automatyczną __name__.

Ale jeśli cały kod znajduje się w funkcji, może to być możliwe. Próbować

import myModule

myModule.main()

Działa to nawet wtedy, gdy moduł chroni się za pomocą pliku __all__.

from myModule import *może nie być mainwidoczny dla Ciebie, więc naprawdę musisz zaimportować sam moduł.


O ok, dzięki za wyjaśnienie. Umieściłem wszystko w funkcji main (), więc powinno być ok. Bardziej interesuje mnie sposób przekazywania argumentów wejściowych do tego „drugiego” głównego. Czy jest jakiś łatwy sposób?
Ricky Robinson

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

To świetne alternatywne wyjaśnienie dostępu, __main__które pomogło mi, dzięki @AaronDigulla
Charlie G

Oto sztuczka, aby wywołać main z innej funkcji: stackoverflow.com/a/20158605/3244382
PatriceG

3

Miałem tę samą potrzebę argparse. Chodzi o to, że parse_argsfunkcja argparse.ArgumentParserinstancji obiektu domyślnie pobiera swoje argumenty z sys.args. Obejście, zgodnie z linią Martijna, polega na wyraźnym wyrażeniu tego, abyś mógł zmienić argumenty, które przekazujesz parse_argsjako pożądanie.

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:])

Kluczową kwestią jest przekazywanie argumentów do parse_argsfunkcji. Później, aby skorzystać z głównego, po prostu postępuj zgodnie z instrukcjami Martijna.


3

Odpowiedź, której szukałem, znalazła się tutaj: Jak używać argumentu Python z argumentami innymi niż sys.argv?

Jeśli main.pyi parse_args()jest napisane w ten sposób, to parsowanie można wykonać ładnie

# 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)

Następnie możesz wywoływać main()i analizować argumenty parser.parse_args(['--input', 'foobar.txt'])w innym skrypcie Pythona:

# 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

Zakładając, że próbujesz przekazać również argumenty wiersza poleceń.

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:])

Dzięki. Właściwie używam argparsezamiast sys.argv. Jak by się to zmieniło w tym przypadku? Poza tym ze skryptu zewnętrznego chcę tylko przekazać kilka argumentów wejściowych, które wpisuje użytkownik, podczas gdy inne argumenty wejściowe dla skryptu wewnętrznego (myModule.py) są przeze mnie zakodowane na stałe.
Ricky Robinson

Nie widząc samego kodu, trudno odpowiedzieć na szczegóły. Ogólnie rzecz biorąc, po prostu przekazujesz wszelkie argumenty. W *rozpakowuje tablicą f(a) => f([1,2,3])vs f(*a) => f(1,2,3) można było tak łatwo zrobić, myModule.main(sys.argv[1], "other value", 39)czy cokolwiek innego.
agoebel
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.