Trong Python, sử dụng argparse, chỉ cho phép các số nguyên dương


164

Tiêu đề khá nhiều tóm tắt những gì tôi muốn xảy ra.

Đây là những gì tôi có, và trong khi chương trình không thổi vào một số nguyên không có chủ đích, tôi muốn người dùng được thông báo rằng một số nguyên không có chủ đích về cơ bản là vô nghĩa.

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

Và đầu ra:

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

Đầu ra có âm:

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

Bây giờ, rõ ràng tôi chỉ có thể thêm một if để xác định if args.gameslà âm tính, nhưng tôi tò mò liệu có cách nào để bẫy nó ở argparsecấp độ không, để tận dụng lợi thế của việc in sử dụng tự động.

Lý tưởng nhất, nó sẽ in một cái gì đó tương tự như thế này:

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'

Thích như vậy:

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'

Hiện tại tôi đang làm điều này và tôi đoán tôi rất vui:

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

Câu trả lời:


244

Điều này nên được sử dụng có thể type. Bạn vẫn sẽ cần xác định một phương thức thực tế quyết định điều này cho bạn:

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)

Đây về cơ bản chỉ là một ví dụ thích nghi từ perfect_squarechức năng trong các tài liệu trên argparse.


1
Hàm của bạn có thể có nhiều giá trị không? Làm thế nào mà làm việc?
Tom

2
Nếu chuyển đổi intthành thất bại, vẫn sẽ có một đầu ra có thể đọc được? Hoặc bạn nên try raisechuyển đổi thủ công cho điều đó?
NOh

4
@MrZ Nó sẽ cung cấp cho một cái gì đó như error: argument foo: invalid check_positive value: 'foo=<whatever>'. Bạn chỉ cần thêm một try:... except ValueError:xung quanh nó sẽ phát sinh một ngoại lệ với thông báo lỗi tốt hơn.
Yuushi

59

type sẽ là tùy chọn được đề xuất để xử lý các điều kiện / kiểm tra, như trong câu trả lời của Yuushi.

Trong trường hợp cụ thể của bạn, bạn cũng có thể sử dụng choicestham số nếu giới hạn trên của bạn cũng được biết:

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

Lưu ý: Sử dụng rangethay vì xrangecho python 3.x


3
Tôi tưởng tượng điều này sẽ khá kém hiệu quả, vì bạn sẽ tạo ra một phạm vi và sau đó đạp xe qua nó để xác nhận đầu vào của bạn. Nhanh iflà nhanh hơn nhiều.
TravisThomas

2
@ trav1th Thật vậy, nhưng đó là cách sử dụng ví dụ từ các tài liệu. Ngoài ra, tôi đã nói trong câu trả lời của mình rằng câu trả lời của Yuushi là câu trả lời. Tốt để đưa ra lựa chọn. Và trong trường hợp argparse, nó xảy ra một lần cho mỗi lần thực thi, sử dụng trình tạo ( xrange) và không yêu cầu mã bổ sung. Sự đánh đổi đó là có sẵn. Lên đến từng người để quyết định con đường nào để đi.
aneroid

16
Để rõ ràng hơn về quan điểm của jgritty về câu trả lời của tác giả, các lựa chọn = xrange (0,1000) sẽ dẫn đến toàn bộ danh sách các số nguyên từ 1 đến 999 được ghi vào bảng điều khiển của bạn mỗi khi bạn sử dụng - trợ giúp hoặc nếu một đối số không hợp lệ là cung cấp. Không phải là một lựa chọn tốt trong hầu hết các trường hợp.
biomiker

9

Cách nhanh chóng và bẩn thỉu, nếu bạn có một mức tối đa có thể dự đoán cũng như tối thiểu cho đối số của bạn, là sử dụng choicesvới một phạm vi

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

24
Nhược điểm là đầu ra gớm ghiếc.
jgritty

6
nhấn mạnh vào bẩn , tôi đoán.
ben tác giả

4
Để rõ ràng hơn về quan điểm của jgritty, tests = xrange (0,1000) sẽ dẫn đến toàn bộ danh sách các số nguyên từ 1 đến 999 được ghi vào bảng điều khiển của bạn mỗi khi bạn sử dụng - trợ giúp hoặc nếu được cung cấp đối số không hợp lệ. Không phải là một lựa chọn tốt trong hầu hết các trường hợp.
biomiker

8

Một cách khác đơn giản hơn, đặc biệt là nếu phân lớp argparse.ArgumentParser, là bắt đầu xác nhận từ bên trong parse_argsphương thức.

Bên trong một lớp con như vậy:

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

Kỹ thuật này có thể không tuyệt vời như một cuộc gọi tùy chỉnh, nhưng nó thực hiện công việc.


Về ArgumentParser.error(message):

Phương pháp này in một thông báo sử dụng bao gồm thông báo đến lỗi tiêu chuẩn và chấm dứt chương trình với mã trạng thái là 2.


Tín dụng: câu trả lời của jonatan


Hoặc ít nhất, thay thế print "-g/--games: must be positive."; sys.exit(1)bằng chỉ parser.error("-g/--games: must be positive."). (Cách sử dụng như trong câu trả lời của jonatan .)
aneroid

3

Trong trường hợp ai đó (như tôi) gặp câu hỏi này trong tìm kiếm của Google, đây là một ví dụ về cách sử dụng phương pháp mô đun để giải quyết gọn gàng vấn đề chung hơn là chỉ cho phép số nguyên argparse trong một phạm vi được chỉ định :

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

Điều này cho phép bạn làm một cái gì đó như:

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

Biến foobây giờ chỉ cho phép các số nguyên dương , như OP đã hỏi.

Lưu ý rằng ngoài các hình thức trên, chỉ có thể tối đa với IntRange:

parser.add_argument('other', type=IntRange(imax=10))  # Must have other <= 10
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.