Чи можу я в 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.