กำลังตรวจสอบว่ามีการกำหนด sys.argv [x] หรือไม่


105

วิธีใดเป็นวิธีที่ดีที่สุดในการตรวจสอบว่ามีการส่งผ่านตัวแปรสำหรับสคริปต์หรือไม่:

try:
    sys.argv[1]
except NameError:
    startingpoint = 'blah'
else:
    startingpoint = sys.argv[1]

คำตอบ:


64

ในท้ายที่สุดความแตกต่างระหว่างtry, exceptและการทดสอบlen(sys.argv)ไม่ได้มีนัยสำคัญทั้งหมด ทั้งคู่ค่อนข้างแฮ็คเมื่อเทียบกับargparse.

สิ่งนี้เกิดขึ้นกับฉัน - เป็นอาร์กิวเมนต์งบประมาณต่ำ:

arg_names = ['command', 'x', 'y', 'operation', 'option']
args = dict(zip(arg_names, sys.argv))

คุณยังสามารถใช้เพื่อสร้าง a namedtupleด้วยค่าที่เป็นค่าเริ่มต้นNone- ทั้งหมดในสี่บรรทัด!

Arg_list = collections.namedtuple('Arg_list', arg_names)
args = Arg_list(*(args.get(arg, None) for arg in arg_names))

ในกรณีที่คุณไม่คุ้นเคยnamedtupleมันเป็นเพียงทูเพิลที่ทำหน้าที่เหมือนวัตถุทำให้คุณสามารถเข้าถึงค่าโดยใช้tup.attributeไวยากรณ์แทนtup[0]ไวยากรณ์

ดังนั้นบรรทัดแรกจึงสร้างnamedtupleชนิดใหม่โดยมีค่าสำหรับแต่ละค่าในarg_names. บรรทัดที่สองส่งผ่านค่าจากargsพจนานุกรมโดยใช้getเพื่อส่งคืนค่าเริ่มต้นเมื่อชื่ออาร์กิวเมนต์ที่ระบุไม่มีค่าที่เกี่ยวข้องในพจนานุกรม


ฉันรัก Python มากขึ้นเรื่อย ๆ ทุกคนต้องขอบคุณคำตอบเช่นนั้นและ Stack Overflow แน่นอน! น่าเสียดายที่เราไม่มีคำอธิบายของสองบรรทัดสุดท้าย ฉันเชื่อว่าสำหรับผู้เริ่มต้นจะดีมาก
Goujon

2
รักเลย! เป็นทางออกที่ดีที่สุดที่ฉันเคยเห็นปัญหานี้ได้อย่างง่ายดาย สำหรับคนอื่น ๆ : มันไม่ชัดเจนสำหรับฉันในทันที (newb) ว่า: 1) 'command' จะส่งผ่านเป็นอาร์กิวเมนต์แรกในแต่ละครั้งและ 2) คุณสามารถเรียกผลลัพธ์ด้วย args [0] เป็นต้น
Matt D

นี่เป็นเรื่องที่ดีอย่างแน่นอน แต่การทดสอบความยาวเป็นอย่างไร
colorlace

1
@timwiz ฉันพูดเปรียบเทียบกับการใช้ argparse เท่านั้น ถึงกระนั้นทุกวันนี้ฉันก็เตะตัวเองทุกครั้งที่กลับไปดูสคริปต์เก่า ๆ และพบว่าฉันไม่ได้ใช้ argparse
ส่ง

117

ตรวจสอบความยาวของsys.argv:

if len(sys.argv) > 1:
    blah = sys.argv[1]
else:
    blah = 'blah'

บางคนชอบวิธีการยกเว้นตามที่คุณแนะนำ (เช่นtry: blah = sys.argv[1]; except IndexError: blah = 'blah') แต่ฉันไม่ชอบมันมากนักเพราะมันไม่ได้ "ปรับขนาด" ได้ดีเท่า (เช่นเมื่อคุณต้องการยอมรับสองหรือสามข้อโต้แย้ง) และมันอาจจะซ่อนข้อผิดพลาด (เช่นหากคุณใช้blah = foo(sys.argv[1])แต่foo(...)ยกIndexErrorที่IndexErrorจะได้รับการละเว้น)


1
try, exceptตราบใดที่คุณกำลังเป็นอะตอมเกี่ยวกับเรื่องนี้ว่ามีอะไรต้องกังวลเกี่ยวกับการเป็น เพียงแค่รอการfooโต้แย้งของคุณจนถึงelseประโยค
ส่ง

3
argparseนอกจากนี้เมื่อคุณความคิดเกี่ยวกับการปรับกำลังก็ถึงเวลาที่จะย้ายไปยัง
ส่ง

1
+1 บน argparse ฉันมีargparse.pyในโครงการบรรทัดคำสั่งทั้งหมดของฉัน
David Wolever

2
@senderle: แน่นอนว่าถ้าคุณขยันขันแข็งก็ไม่เป็นไร แต่จากประสบการณ์ของฉันโปรแกรมเมอร์ส่วนใหญ่ไม่ขยันและใส่ตรรกะทั้งหมดไว้ในtryประโยค ... ดังนั้นเนื่องจากว่ามันไม่ยาก (และ IMO สวยกว่าเล็กน้อย) ฉันชอบที่จะตรวจสอบความยาวเท่านั้น (นอกจากนี้คุณหลีกเลี่ยงการมีคน ตะโกนใส่คุณที่ใช้ข้อยกเว้นสำหรับการควบคุมการไหล)
David Wolever

2
@ เดวิดใช่ฉันเห็นประเด็นของคุณ ฉันเป็นคนtryขอโทษฉันต้องยอมรับ ฉันแค่รู้สึกว่าบางครั้งมันแสดงเจตนาของฉันชัดเจนกว่าifคำพูด
ส่ง

22

อีกวิธีหนึ่งที่ฉันยังไม่เห็นในรายการคือการตั้งค่า Sentinel ของคุณล่วงหน้า วิธีนี้ใช้ประโยชน์จากการประเมินแบบขี้เกียจของ Python ซึ่งคุณไม่จำเป็นต้องระบุelseคำสั่งเสมอไป ตัวอย่าง:

startingpoint = 'blah'
if len(sys.argv) >= 2:
  startingpoint = sys.argv[1]

หรือถ้าคุณกำลังใช้ Syntax CRAZY คุณสามารถใช้ตัวดำเนินการ ternaryของ Python :

startingpoint = sys.argv[1] if len(sys.argv) >= 2 else 'blah'

1
คำตอบของตัวดำเนินการที่เกี่ยวข้องกับสายตาของฉันคือคนที่สวยที่สุด ฉันนั่งอยู่ตรงนี้เงียบ ๆ สาปแช่งความจริงที่ว่าซอฟต์แวร์บางตัวที่ฉันดูแลนั้นเชื่อมโยงกับ Python 2.4 ซึ่งไม่มี
Richard Barrell

12

ฉันใช้สิ่งนี้ - มันไม่เคยล้มเหลว:

startingpoint = 'blah'
if sys.argv[1:]:
   startingpoint = sys.argv[1]

นั่นไม่ใช่การตรวจสอบหากกำหนดไว้ เป็นเหมือนคำจำกัดความถ้ามีอยู่ซึ่งไม่เหมือนกัน ฉันใช้วิธีนั้นเช่นกันในการยอมรับตัวแปรทางเลือก แต่ไม่ใช่คำตอบสำหรับคำถามปัจจุบัน
m3nda

@ erm3nda ฉันสามารถลบstartingpointตัวแปรออกจากตัวอย่างได้ แต่คำถามคือการกำหนดตัวแปรทางเลือกดังนั้นฉันจึงทำแบบเดียวกัน
anatoly techtonik

1
หากคุณลบออกคุณจะได้รับข้อผิดพลาดเกี่ยวกับตัวแปรที่ไม่ได้กำหนดไว้ ฉันได้อ่านคำถามอีกครั้งและสิ่งที่เขาคาดหวังก็คือการแทนที่ตัวแปร :) ดังนั้นก็โอเค ขอขอบคุณคุณสำหรับคำแนะนำจากทางสั้น ๆ sys.argv[1:]:ว่าหาก สิ่งนี้ใช้ได้กับอาร์กิวเมนต์ตำแหน่งในขณะที่ไม่นับ
m3nda

10

มันเป็นรายการ Python ธรรมดา ข้อยกเว้นที่คุณจะจับได้คือ IndexError แต่คุณควรตรวจสอบความยาวแทน

if len(sys.argv) >= 2:
  startingpoint = sys.argv[1]
else:
  startingpoint = 'blah'

2

โซลูชันที่ทำงานร่วมกับ fonction ในตัวแผนที่!

arg_names = ['command' ,'operation', 'parameter']
args = map(None, arg_names, sys.argv)
args = {k:v for (k,v) in args}

จากนั้นคุณต้องเรียกพารามิเตอร์ของคุณดังนี้:

if args['operation'] == "division":
    if not args['parameter']:
        ...
    if args['parameter'] == "euclidian":
        ...

1

คุณสามารถต่อท้ายค่าของ argv [1] กับ argv แล้วตรวจสอบว่า argv [1] ไม่เท่ากับสตริงที่คุณป้อนเข้าไปหรือไม่ตัวอย่าง:

from sys import argv
argv.append('SomeString')
if argv[1]!="SomeString":
            print(argv[1])

สำหรับผู้ที่ลงคะแนนให้ฉันอับอายคุณที่ไม่ชี้แจงข้อผิดพลาด
Hassan Abdul-Kareem

ฉันเดาว่าเป็นเพราะมันแฮ็ค จะเกิดอะไรขึ้นถ้ามีคนผ่าน SomeString เป็นอาร์กิวเมนต์? จะใช้สิ่งนั้นได้อย่างไรหากคุณสามารถยอมรับข้อโต้แย้ง 1 ถึง 5 ได้
pbogut

เจ๋ง (แต่ยังแฮ็ค) สำหรับการตั้งค่าเริ่มต้นในกรณีที่ผู้ใช้ไม่ได้ระบุอาร์กิวเมนต์ เพียงต่อท้ายแล้วใช้ argv [1]
Felizett

1

ค่อนข้างใกล้เคียงกับสิ่งที่ผู้ริเริ่มพยายามทำ นี่คือฟังก์ชั่นที่ฉันใช้:

def get_arg(index):
    try:
        sys.argv[index]
    except IndexError:
        return ''
    else:
        return sys.argv[index]

ดังนั้นการใช้งานจะเป็นดังนี้:

if __name__ == "__main__":
    banner(get_arg(1),get_arg(2))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.