Чи можу я в Python викликати main () імпортованого модуля?


86

У Python у мене є модуль myModule.py, де я визначаю кілька функцій і main () , який приймає кілька аргументів командного рядка.

Зазвичай я називаю це main () із сценарію bash. Тепер я хотів би помістити все в невеликий пакет , тому я подумав, що, можливо, я можу перетворити мій простий скрипт bash на скрипт Python і помістити його в пакет.

Отже, як мені насправді викликати функцію main () myModule.py з функції main () MyFormerBashScript.py? Чи можу я це зробити? Як йому передати будь-які аргументи ?


Якщо ви імпортували myModule, ви зможете зателефонувати myModule.main(). Що ви пробували дотепер?
Маріус

Мене турбують вхідні аргументи, які я зазвичай передаю із сценарію оболонки.
Ricky Robinson

Чи є сенс викликати це з subprocessмодулем?
BenDundee

Я думаю, це було б простіше, так.
Ricky Robinson

Відповіді:


114

Це просто функція. Імпортуйте його і зателефонуйте:

import myModule

myModule.main()

Якщо вам потрібно проаналізувати аргументи, у вас є два варіанти:

  • Проаналізуйте їх main(), але передайте sys.argvяк параметр (весь код нижче в тому самому модулі myModule):

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

    Тепер ви можете імпортувати та дзвонити myModule.main(['arg1', 'arg2', 'arg3'])з іншого модуля.

  • Є main()приймати параметри, які вже розібрані (знову весь код в myModuleмодулі):

    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)
    

    і імпортувати та викликати в myModule.main(foovalue, barvalue, baz='ham')іншому місці та передаючи аргументи python за потреби.

Фокус тут полягає у виявленні, коли ваш модуль використовується як скрипт; коли ви запускаєте файл python як основний скрипт ( python filename.py), жоден importоператор не використовується, тому python викликає цей модуль "__main__". Але якщо той самий filename.pyкод трактується як модуль ( import filename), тоді python використовує його як ім'я модуля. В обох випадках змінна __name__встановлена, і тестування на неї повідомляє, як запускався ваш код.


3
Звичайно. Але як щодо вхідних аргументів? Я використовую argparse, тому коли я викликаю сценарій із терміналу, я це роблю $ python myModule -a input_a -b input_b --parameterC input_c. Як це буде працювати в коді python? Це те, чого я не зміг знайти за допомогою простого пошуку.
Рікі Робінсон,

@RickyRobinson: розширено, щоб показати, що ви можете мати це в обох напрямках; просто передайте аргументи, проаналізовані або аналізовані.
Мартін Пітерс

1
Дякую. Не могли б Ви також вказати, який фрагмент коду належить до якого модуля чи сценарію? Це виглядає набагато громіздкішим, ніж те, що я думав спочатку.
Ricky Robinson

@RickyRobinson: Обидва фрагменти належать разом в одному модулі; Я це чітко заявив.
Мартін Пітерс

42

Відповідь Мартієна має сенс, але в ньому бракувало чогось вирішального, що може здатися очевидним для інших, але мені було важко це зрозуміти.

У версії, де ви використовуєте argparse, вам потрібно мати цей рядок в основному тексті.

args = parser.parse_args(args)

Зазвичай, коли ви використовуєте argparse просто в сценарії, ви просто пишете

args = parser.parse_args()

і parse_args знаходять аргументи з командного рядка. Але в цьому випадку основна функція не має доступу до аргументів командного рядка, тому вам слід сказати argparse, якими є аргументи.

Ось приклад

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

Якщо припустити, що ви вказали цю назву mytest.py, щоб запустити її, ви можете зробити будь-яку з них із командного рядка

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

який повертається відповідно

X center: 8.0
Y center: 4

або

X center: 8.0
Y center: 2.0

або

X center: 2
Y center: 4

Або якщо ви хочете запустити з іншого сценарію python, ви можете це зробити

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

який повертається

X center: 7.0
Y center: 6.0

4
Це саме те, що мені потрібно - велике спасибі за корисний додаток
HFBrowning

Це могло працювати з Python 2, але в Python 3 це вже не працює, не можна викликати функцію main () модуля:AttributeError: module 'sqlacodegen' has no attribute 'main'
NaturalBornCamper

24

Це залежить. Якщо основний код захищений ifяк у:

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

тоді ні, ви не можете змусити Python виконати це, оскільки ви не можете впливати на автоматичну змінну __name__.

Але коли весь код знаходиться у функції, тоді це можливо. Спробуйте

import myModule

myModule.main()

Це працює навіть тоді, коли модуль захищає себе за допомогою __all__.

from myModule import *може не зробити вас mainвидимим, тому вам дійсно потрібно імпортувати сам модуль.


О гаразд, дякую за роз'яснення. Я помістив усе у функцію main (), тому це повинно бути нормально. Мене більше турбує питання про те, як передати вхідні аргументи до цього "другого" основного. Будь-який простий спосіб зробити це?
Рікі Робінсон,

Звичайно:import sys; module.main(sys.argv);
Аарон Дігулла

Це чудове альтернативне пояснення доступу, __main__яке мені допомогло, дякую @AaronDigulla
Charlie G

Ось трюк назвати основні з іншої функції: stackoverflow.com/a/20158605/3244382
PatriceG

3

У мене була така сама потреба у використанні argparse. Річ у parse_argsфункції argparse.ArgumentParserекземпляра об'єкта неявно бере свої аргументи за замовчуванням з sys.args. Обійстя, слідуючи рядку Мартіна, полягає у тому, щоб зробити це явним, тому ви можете змінити аргументи, які ви передаєте parse_argsяк бажання.

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

Ключовим моментом є передача аргументів для parse_argsфункціонування. Пізніше, щоб використовувати головне, ви просто робите те, що розповідає Мартін.


3

Відповідь, яку я шукав, отримав відповідь тут: Як використовувати python argparse з аргументами, відмінними від sys.argv?

Якщо main.pyі parse_args()написано таким чином, то синтаксичний аналіз можна зробити красиво

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

Потім ви можете викликати main()та аналізувати аргументи з parser.parse_args(['--input', 'foobar.txt'])ним в іншому скрипті 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

Припускаючи, що ви також намагаєтесь передати аргументи командного рядка.

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

Дякую. Я фактично використовую argparseзамість sys.argv. Як би це змінилося в цьому випадку? Крім того, із зовнішнього сценарію я просто хочу передати кілька вхідних аргументів, які вводить користувач, тоді як інші вхідні аргументи для внутрішнього сценарію (myModule.py) жорстко кодовані мною.
Ricky Robinson

Не бачачи самого коду, важко відповісти на конкретні питання. Загалом, ви б просто передали будь-які аргументи. У *розпаковується масив f(a) => f([1,2,3])проти f(*a) => f(1,2,3) вас може так само легко зробити myModule.main(sys.argv[1], "other value", 39)або будь-який інший .
agoebel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.