ใน Python ฉันสามารถเรียก main () ของโมดูลที่นำเข้าได้หรือไม่


87

ใน Python ฉันมีโมดูล myModule.py ที่ฉันกำหนดฟังก์ชันสองสามอย่างและmain ()ซึ่งใช้อาร์กิวเมนต์บรรทัดคำสั่งสองสามข้อ

ฉันมักจะเรียกสิ่งนี้ว่า main () จาก bash script ตอนนี้ฉันต้องการใส่ทุกอย่างลงในแพ็คเกจขนาดเล็กดังนั้นฉันจึงคิดว่าบางทีฉันอาจจะเปลี่ยน bash script ธรรมดาของฉันให้เป็นสคริปต์ Python แล้วใส่ลงในแพ็คเกจ

ดังนั้นฉันจะเรียกฟังก์ชันmain ()ของ myModule.py จากฟังก์ชัน main ()ของ MyFormerBashScript.py ได้อย่างไร ฉันสามารถทำได้หรือไม่? ฉันจะส่งผ่านข้อโต้แย้งใด ๆไปได้อย่างไร


หากคุณนำเข้า myModule คุณจะสามารถโทรmyModule.main()ได้ คุณได้ลองทำอะไรมาบ้าง?
Marius

ฉันกังวลกับอาร์กิวเมนต์อินพุตซึ่งฉันมักจะส่งผ่านจากเชลล์สคริปต์
Ricky Robinson

มันสมเหตุสมผลหรือไม่ที่จะเรียกมันด้วยsubprocessโมดูล?
BenDundee

ฉันเดาว่ามันจะง่ายกว่าใช่
Ricky Robinson

คำตอบ:


116

มันเป็นเพียงฟังก์ชั่น นำเข้าและเรียกมันว่า:

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 code? นั่นคือสิ่งที่ฉันไม่สามารถหาได้จากการค้นหาง่ายๆ
Ricky Robinson

@RickyRobinson: ขยายเพื่อแสดงว่าคุณสามารถมีได้ทั้งสองวิธี เพียงแค่ส่งผ่านอาร์กิวเมนต์ที่แยกวิเคราะห์หรือเพื่อแยกวิเคราะห์
Martijn Pieters

1
ขอบคุณ. โปรดระบุรหัสที่ตัดตอนมาว่าเป็นของโมดูลหรือสคริปต์ใด มันดูยุ่งยากกว่าที่คิดไว้ตอนแรก
Ricky Robinson

@RickyRobinson: ข้อความที่ตัดตอนมาทั้งสองอยู่ด้วยกันในโมดูลเดียวกัน ฉันได้ระบุไว้อย่างชัดเจน
Martijn Pieters

42

คำตอบของ Martijen นั้นสมเหตุสมผล แต่มันขาดบางอย่างที่สำคัญซึ่งอาจดูเหมือนชัดเจนสำหรับคนอื่น แต่ก็ยากสำหรับฉันที่จะคิดออก

ในเวอร์ชันที่คุณใช้ argparse คุณต้องมีบรรทัดนี้ในเนื้อหาหลัก

args = parser.parse_args(args)

โดยปกติเมื่อคุณใช้ argparse ในสคริปต์ที่คุณเพิ่งเขียน

args = parser.parse_args()

และ parse_args ค้นหาอาร์กิวเมนต์จากบรรทัดคำสั่ง แต่ในกรณีนี้ฟังก์ชันหลักไม่สามารถเข้าถึงอาร์กิวเมนต์บรรทัดคำสั่งได้ดังนั้นคุณต้องบอกอาร์กิวเมนต์ว่าอาร์กิวเมนต์คืออะไร

นี่คือตัวอย่าง

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

26

มันขึ้นอยู่กับ. หากรหัสหลักได้รับการป้องกันโดยifas in:

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

__name__แล้วไม่มีคุณไม่สามารถทำให้งูหลามที่ดำเนินการเพราะคุณไม่สามารถมีอิทธิพลต่อตัวแปรอัตโนมัติ

แต่เมื่อรหัสทั้งหมดอยู่ในฟังก์ชันแล้วก็อาจสามารถทำได้ ลอง

import myModule

myModule.main()

สิ่งนี้ใช้ได้แม้ว่าโมดูลจะป้องกันตัวเองด้วยไฟล์__all__.

from myModule import *อาจไม่mainปรากฏให้คุณเห็นดังนั้นคุณต้องนำเข้าโมดูลเองจริงๆ


โอเคขอบคุณสำหรับคำชี้แจง ฉันใส่ทุกอย่างในฟังก์ชัน main () ดังนั้นมันก็น่าจะโอเค ฉันกังวลมากขึ้นเกี่ยวกับวิธีส่งอาร์กิวเมนต์อินพุตไปยังหลัก "ที่สอง" นี้ มีวิธีง่ายๆไหม
Ricky Robinson

แน่นอน:import sys; module.main(sys.argv);
Aaron Digulla

นี่เป็นคำอธิบายทางเลือกที่ดีเกี่ยวกับการเข้าถึง__main__ซึ่งช่วยฉันได้ขอบคุณ @AaronDigulla
Charlie G

นี่คือเคล็ดลับในการเรียก main จากฟังก์ชันอื่น: stackoverflow.com/a/20158605/3244382
PatriceG

3

ฉันมีความต้องการเหมือนกันโดยใช้argparseด้วย สิ่งที่เป็นparse_argsฟังก์ชั่นของวัตถุเช่นโดยปริยายใช้อาร์กิวเมนต์ของตนโดยเริ่มต้นจากargparse.ArgumentParser sys.argsวิธีแก้ปัญหาตามบรรทัด Martijn ประกอบด้วยการทำให้ชัดเจนเพื่อให้คุณสามารถเปลี่ยนอาร์กิวเมนต์ที่คุณส่งผ่านไป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:])

ประเด็นสำคัญคือการส่ง args ไปยังparse_argsฟังก์ชัน ต่อมาในการใช้หลักคุณก็ทำตามที่ Martijn บอก


3

คำตอบที่ฉันกำลังค้นหามีคำตอบที่นี่: จะใช้ python argparse กับ args อื่นที่ไม่ใช่ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.