ใน Python การใช้ argparse อนุญาตเฉพาะจำนวนเต็มบวกเท่านั้น


164

ชื่อค่อนข้างจะสรุปสิ่งที่ฉันต้องการให้เกิดขึ้น

นี่คือสิ่งที่ฉันมีและในขณะที่โปรแกรมไม่ระเบิดจำนวนเต็ม nonpositive ฉันต้องการให้ผู้ใช้ทราบว่าจำนวนเต็มที่ไม่ใช่เชิงลบนั้นเป็นเรื่องไร้สาระ

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-g", "--games", type=int, default=162,
                    help="The number of games to simulate")
args = parser.parse_args()

และผลลัพธ์:

python simulate_many.py -g 20
Setting up...
Playing games...
....................

เอาต์พุตที่มีค่าลบ:

python simulate_many.py -g -2
Setting up...
Playing games...

ตอนนี้เห็นได้ชัดว่าฉันสามารถเพิ่มถ้าเพื่อตรวจสอบว่าif args.gamesเป็นเชิงลบ แต่ฉันอยากรู้ว่ามีวิธีที่จะดักมันในargparseระดับเพื่อใช้ประโยชน์จากการพิมพ์การใช้งานอัตโนมัติ

เป็นการดีที่มันจะพิมพ์สิ่งที่คล้ายกับนี้:

python simulate_many.py -g a
usage: simulate_many.py [-h] [-g GAMES] [-d] [-l LEAGUE]
simulate_many.py: error: argument -g/--games: invalid int value: 'a'

ชอบมาก

python simulate_many.py -g -2
usage: simulate_many.py [-h] [-g GAMES] [-d] [-l LEAGUE]
simulate_many.py: error: argument -g/--games: invalid positive int value: '-2'

สำหรับตอนนี้ฉันกำลังทำสิ่งนี้และฉันคิดว่าฉันมีความสุข:

if args.games <= 0:
    parser.print_help()
    print "-g/--games: must be positive."
    sys.exit(1)

คำตอบ:


244

typeนี้ควรจะเป็นไปได้ใช้ คุณจะต้องกำหนดวิธีการจริงที่จะตัดสินใจเลือกสิ่งนี้สำหรับคุณ:

def check_positive(value):
    ivalue = int(value)
    if ivalue <= 0:
        raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)
    return ivalue

parser = argparse.ArgumentParser(...)
parser.add_argument('foo', type=check_positive)

นี้เป็นเพียงตัวอย่างที่ดัดแปลงมาจากperfect_squareฟังก์ชั่นในเอกสารargparseได้ที่


1
ฟังก์ชั่นของคุณสามารถมีหลายค่าได้หรือไม่? มันทำงานอย่างไร
Tom

2
หากการแปลงintล้มเหลวจะยังคงมีเอาต์พุตที่อ่านได้หรือไม่? หรือคุณควรtry raiseแปลงด้วยตนเองสำหรับที่?
NOhs

4
@MrZ error: argument foo: invalid check_positive value: 'foo=<whatever>'มันจะให้สิ่งที่ต้องการ คุณสามารถเพิ่มtry:... ที่except ValueError:อยู่ใกล้ ๆ ซึ่งจะยกข้อยกเว้นขึ้นใหม่ด้วยข้อความแสดงข้อผิดพลาดที่ดีกว่า
Yuushi

59

type จะเป็นตัวเลือกที่แนะนำในการจัดการกับเงื่อนไข / เช็คเช่นเดียวกับในคำตอบของ Yuushi

ในกรณีเฉพาะของคุณคุณยังสามารถใช้choicesพารามิเตอร์หากเป็นที่รู้จักขีด จำกัด บนของคุณ:

parser.add_argument('foo', type=int, choices=xrange(5, 10))

หมายเหตุ:ใช้rangeแทนxrangepython 3.x


3
ฉันคิดว่าสิ่งนี้จะไม่มีประสิทธิภาพพอสมควรเนื่องจากคุณจะสร้างช่วงและขี่จักรยานผ่านการตรวจสอบข้อมูลของคุณ รวดเร็วifเป็นได้เร็วขึ้นมาก
TravisThomas

2
@ trav1th อาจเป็นไปได้ แต่เป็นตัวอย่างการใช้งานจากเอกสาร นอกจากนี้ฉันยังได้กล่าวในคำตอบของฉันว่าคำตอบของ Yuushi คือคำตอบที่จะไป ดีที่จะให้ตัวเลือก และในกรณีที่มีการโต้แย้งเกิดขึ้นหนึ่งครั้งต่อการดำเนินการใช้ตัวสร้าง ( xrange) และไม่ต้องการรหัสเพิ่มเติม การแลกเปลี่ยนนั้นมีอยู่ ขึ้นอยู่กับแต่ละคนที่จะตัดสินใจว่าจะไปทางไหน
aneroid

16
เพื่อให้ชัดเจนยิ่งขึ้นเกี่ยวกับจุดของ jgritty ในคำตอบของผู้เขียนตัวเลือก = xrange (0,1000) จะส่งผลให้รายการจำนวนเต็มทั้งหมดจาก 1 ถึง 999 รวมถูกเขียนไปยังคอนโซลของคุณทุกครั้งที่คุณใช้ - ช่วยหรือถ้าอาร์กิวเมนต์ที่ไม่ถูกต้องคือ ให้. ไม่ใช่ตัวเลือกที่ดีในสถานการณ์ส่วนใหญ่
biomiker

9

วิธีที่รวดเร็วและสกปรกหากคุณมีค่าสูงสุดที่คาดเดาได้และค่า min สำหรับ arg ของคุณจะใช้choicesกับช่วง

parser.add_argument('foo', type=int, choices=xrange(0, 1000))

24
ข้อเสียคือผลลัพธ์ที่น่าเกลียด
jgritty

6
เน้นการสกปรกผมคิดว่า
ผู้เขียนเบ็

4
เพื่อให้ชัดเจนเกี่ยวกับจุดของ jgritty ตัวเลือก = xrange (0,1000) จะส่งผลให้รายการทั้งหมดของจำนวนเต็มตั้งแต่ 1 ถึง 999 รวมถึงการเขียนไปยังคอนโซลของคุณทุกครั้งที่คุณใช้ - ช่วยหรือหากมีการโต้แย้งที่ไม่ถูกต้อง ไม่ใช่ตัวเลือกที่ดีในสถานการณ์ส่วนใหญ่
biomiker

8

ทางเลือกที่ง่ายกว่าโดยเฉพาะถ้า subclassing argparse.ArgumentParserคือการเริ่มต้นการตรวจสอบความถูกต้องจากภายในparse_argsเมธอด

ภายในคลาสย่อยดังกล่าว:

def parse_args(self, args=None, namespace=None):
    """Parse and validate args."""
    namespace = super().parse_args(args, namespace)
    if namespace.games <= 0:
         raise self.error('The number of games must be a positive integer.')
    return namespace

เทคนิคนี้อาจไม่เจ๋งเท่าที่กำหนดได้ แต่จะทำงานได้ดี


เกี่ยวกับArgumentParser.error(message):

วิธีการนี้จะพิมพ์ข้อความการใช้งานรวมถึงข้อความไปยังข้อผิดพลาดมาตรฐานและยุติโปรแกรมด้วยรหัสสถานะ 2


เครดิต: ตอบโดย jonatan


หรืออย่างน้อยที่สุดแทนที่มีเพียงprint "-g/--games: must be positive."; sys.exit(1) (การใช้งานเหมือนในคำตอบของโจนาธาน)parser.error("-g/--games: must be positive.")
aneroid

3

ในกรณีที่บางคน (เช่นฉัน) พบกับคำถามนี้ในการค้นหาของ Google นี่คือตัวอย่างของวิธีการใช้วิธีการแยกส่วนเพื่อแก้ไขปัญหาทั่วไปที่เป็นระเบียบมากขึ้นในการอนุญาตให้จำนวนเต็ม argparse ในช่วงที่กำหนดเท่านั้น :

# Custom argparse type representing a bounded int
class IntRange:

    def __init__(self, imin=None, imax=None):
        self.imin = imin
        self.imax = imax

    def __call__(self, arg):
        try:
            value = int(arg)
        except ValueError:
            raise self.exception()
        if (self.imin is not None and value < self.imin) or (self.imax is not None and value > self.imax):
            raise self.exception()
        return value

    def exception(self):
        if self.imin is not None and self.imax is not None:
            return argparse.ArgumentTypeError(f"Must be an integer in the range [{self.imin}, {self.imax}]")
        elif self.imin is not None:
            return argparse.ArgumentTypeError(f"Must be an integer >= {self.imin}")
        elif self.imax is not None:
            return argparse.ArgumentTypeError(f"Must be an integer <= {self.imax}")
        else:
            return argparse.ArgumentTypeError("Must be an integer")

สิ่งนี้ช่วยให้คุณทำสิ่งที่ชอบ:

parser = argparse.ArgumentParser(...)
parser.add_argument('foo', type=IntRange(1))     # Must have foo >= 1
parser.add_argument('bar', type=IntRange(1, 7))  # Must have 1 <= bar <= 7

fooตอนนี้ตัวแปรอนุญาตเฉพาะจำนวนเต็มบวกเท่านั้นเช่น OP ที่ถาม

โปรดทราบว่านอกเหนือจากแบบฟอร์มด้านบนแล้วยังมีความเป็นไปได้สูงสุดด้วยIntRange:

parser.add_argument('other', type=IntRange(imax=10))  # Must have other <= 10
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.