Làm thế nào để triển khai tùy chọn --verbose hoặc -v vào một tập lệnh?


94

Tôi biết --verbosehoặc -vtừ một số công cụ và tôi muốn triển khai điều này thành một số tập lệnh và công cụ của riêng mình.

Tôi đã nghĩ đến việc đặt:

if verbose:
    print ...

thông qua mã nguồn của tôi, để nếu người dùng chuyển -vtùy chọn, biến verbosesẽ được đặt thành Truevà văn bản sẽ được in.

Đây có phải là cách tiếp cận đúng hay có một cách phổ biến hơn?

Bổ sung: Tôi không yêu cầu cách triển khai phân tích cú pháp các đối số. Điều đó tôi biết nó được thực hiện như thế nào. Tôi chỉ đặc biệt quan tâm đến tùy chọn dài dòng.


9
Tại sao không sử dụng mô-đun ghi nhật ký và đặt INFO cấp nhật ký theo mặc định, và GỠ LỖI khi --verbose được chuyển qua? Tốt nhất không nên thực hiện lại bất cứ điều gì đã có sẵn bằng ngôn ngữ ...
Tim

3
@Tim, tôi đồng ý, nhưng mô-đun ghi nhật ký khá khó khăn.
mlissner

Câu trả lời:


106

Đề xuất của tôi là sử dụng một chức năng. Nhưng thay vì đưa ifchức năng vào, điều mà bạn có thể muốn làm, hãy làm như thế này:

if verbose:
    def verboseprint(*args):
        # Print each argument separately so caller doesn't need to
        # stuff everything to be printed into a single string
        for arg in args:
           print arg,
        print
else:   
    verboseprint = lambda *a: None      # do-nothing function

(Có, bạn có thể xác định một hàm trong một ifcâu lệnh và nó sẽ chỉ được xác định nếu điều kiện là đúng!)

Nếu bạn đang sử dụng Python 3, nơi printđã là một hàm (hoặc nếu bạn sẵn sàng sử dụng printnhư một hàm trong 2.x bằng cách sử dụng from __future__ import print_function) thì nó thậm chí còn đơn giản hơn:

verboseprint = print if verbose else lambda *a, **k: None

Bằng cách này, hàm được định nghĩa là không làm gì nếu chế độ tiết bị tắt (sử dụng lambda), thay vì liên tục kiểm tra verbosecờ.

Nếu người dùng có thể thay đổi chế độ chi tiết trong quá trình chạy chương trình của bạn, thì đây sẽ là cách tiếp cận sai (bạn cần có iftrong hàm), nhưng vì bạn đang đặt nó bằng cờ dòng lệnh, bạn chỉ cần đưa ra quyết định một lần.

Sau đó, bạn sử dụng vd verboseprint("look at all my verbosity!", object(), 3)bất cứ khi nào bạn muốn in một thông báo "dài dòng".


1
Tốt hơn nữa, hãy làm như printchức năng: Chấp nhận nhiều đối số. Nó có thể được thực hiện như print(*args)trong 3.x và như for arg in args: print arg,trong 2.x. Ưu điểm chính là nó cho phép trộn các chuỗi và những thứ thuộc các loại khác trong một tin nhắn mà không cần strgọi / định dạng và nối rõ ràng .

Dấu phẩy ở cuối print arg,dòng dùng để làm gì?
SamK

Điều đó dễ dàng xác định cho bản thân của một người bằng thực nghiệm hoặc bằng cách kiểm tra tài liệu, nhưng nó ngăn chặn ngắt dòng thường được in.
kindall

5
Hàm in trong Python 3 cũng có đối số từ khóa tùy chọn, do đó, để tái tạo đầy đủ chức năng của in:def verboseprint(*args, **kwargs): print(*args, **kwargs)
lstyls

61

Sử dụng loggingmô-đun:

import logging as log

args = p.parse_args()
if args.verbose:
    log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
    log.info("Verbose output.")
else:
    log.basicConfig(format="%(levelname)s: %(message)s")

log.info("This should be verbose.")
log.warning("This is a warning.")
log.error("This is an error.")

Tất cả những điều này sẽ tự động chuyển đến stderr:

% python myprogram.py
WARNING: This is a warning.
ERROR: This is an error.

% python myprogram.py -v
INFO: Verbose output.
INFO: This should be verbose.
WARNING: This is a warning.
ERROR: This is an error.

Để biết thêm thông tin, hãy xem Tài liệu Python và các hướng dẫn .


8
Theo Tài liệu Python ở đây , không nên sử dụng ghi nhật ký trong trường hợp bạn chỉ yêu cầu in đầu ra trong quá trình thực thi bình thường của chương trình. Có vẻ như đó là những gì OP muốn.
SANDeveloper

1
Điều này có vẻ ổn đối với vấn đề cơ bản nhưng nhiều lệnh * nix cũng hỗ trợ nhiều cấp độ chi tiết (-v -v -v, v.v.), điều này có thể trở nên lộn xộn theo cách này.
TextGeek

12

Xây dựng và đơn giản hóa câu trả lời của @ kindall, đây là những gì tôi thường sử dụng:

v_print = None
def main()
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbosity', action="count", 
                        help="increase output verbosity (e.g., -vv is more than -v)")

    args = parser.parse_args()

    if args.verbosity:
        def _v_print(*verb_args):
            if verb_args[0] > (3 - args.verbosity):
                print verb_args[1]  
    else:
        _v_print = lambda *a: None  # do-nothing function

    global v_print
    v_print = _v_print

if __name__ == '__main__':
    main()

Sau đó, điều này cung cấp cách sử dụng sau trong suốt tập lệnh của bạn:

v_print(1, "INFO message")
v_print(2, "WARN message")
v_print(3, "ERROR message")

Và tập lệnh của bạn có thể được gọi như thế này:

% python verbose-tester.py -v
ERROR message

% python verbose=tester.py -vv
WARN message
ERROR message

% python verbose-tester.py -vvv
INFO message
WARN message
ERROR message

Một vài lưu ý:

  1. Đối số đầu tiên của bạn là mức độ lỗi của bạn và đối số thứ hai là thông báo của bạn. Nó có con số kỳ diệu là3 đặt giới hạn trên cho việc ghi nhật ký của bạn, nhưng tôi chấp nhận điều đó như một sự thỏa hiệp vì sự đơn giản.
  2. Nếu bạn muốn v_printlàm việc trong suốt chương trình của mình, bạn phải làm việc chung với toàn cầu. Không có gì vui cả, nhưng tôi thách thức ai đó tìm ra cách tốt hơn.

1
Tại sao bạn không sử dụng mô-đun ghi nhật ký cho INFO và WARN? Đó là nhập nó khi -vđược sử dụng. Trong giải pháp hiện tại của bạn, mọi thứ được chuyển sang stdout thay vì stderr. Và: bạn thường muốn chuyển tiếp mọi lỗi cho người dùng, phải không?
Profpatsch

2
Vâng, đó là một điểm công bằng. Ghi nhật ký có một số chi phí nhận thức mà tôi đang cố gắng tránh, nhưng có lẽ đó là điều "đúng đắn" cần làm. Nó chỉ làm phiền tôi trong quá khứ ...
mlissner

9

Những gì tôi làm trong các tập lệnh của mình là kiểm tra trong thời gian chạy nếu tùy chọn 'verbose' được đặt và sau đó đặt mức ghi nhật ký của tôi thành gỡ lỗi. Nếu nó không được đặt, tôi đặt nó thành thông tin. Bằng cách này, bạn không có kiểm tra 'nếu tiết' trên toàn bộ mã của mình.


2

Nó có thể gọn gàng hơn nếu bạn có một hàm, chẳng hạn như được gọi vprint, kiểm tra cờ chi tiết cho bạn. Sau đó, bạn chỉ cần gọi vprinthàm của riêng mình bất kỳ nơi nào bạn muốn độ dài tùy chọn.



2

Giải pháp của @ kindall không hoạt động với phiên bản Python 3.5 của tôi. @styles tuyên bố chính xác trong nhận xét của anh ấy rằng lý do là đối số từ khóa tùy chọn bổ sung . Do đó, phiên bản được tinh chỉnh một chút của tôi cho Python 3 trông như thế này:

if VERBOSE:
    def verboseprint(*args, **kwargs):
        print(*args, **kwargs)
else:
    verboseprint = lambda *a, **k: None # do-nothing function

1

Có thể có một biến toàn cục, có thể được đặt bằng argparsefrom sys.argv, viết tắt của việc chương trình có nên dài dòng hay không. Sau đó, một trình trang trí có thể được viết sao cho nếu tính chi tiết được bật, thì đầu vào chuẩn sẽ được chuyển hướng sang vùng nhớ trống miễn là hàm vẫn chạy:

import os
from contextlib import redirect_stdout
verbose = False

def louder(f):
    def loud_f(*args, **kwargs):
        if not verbose:
            with open(os.devnull, 'w') as void:
                with redirect_stdout(void):
                    return f(*args, **kwargs)
        return f(*args, **kwargs)
    return loud_f

@louder
def foo(s):
    print(s*3)

foo("bar")

Câu trả lời này được lấy cảm hứng từ mã này ; thực ra, tôi sẽ chỉ sử dụng nó như một mô-đun trong chương trình của mình, nhưng tôi gặp lỗi mà tôi không thể hiểu được, vì vậy tôi đã điều chỉnh một phần của nó.

Nhược điểm của giải pháp này là độ dài là nhị phân, không giống như với logging, cho phép điều chỉnh tốt hơn mức độ dài của chương trình. Ngoài ra, tất cả các print cuộc gọi đều bị chuyển hướng, điều này có thể không mong muốn.


0

Những gì tôi cần là một hàm in ra một đối tượng (obj), nhưng chỉ khi biến toàn cục dài dòng là true, nếu không nó không làm gì cả.

Tôi muốn có thể thay đổi tham số toàn cục "verbose" bất kỳ lúc nào. Đối với tôi, sự đơn giản và dễ đọc là điều tối quan trọng. Vì vậy, tôi sẽ tiếp tục như những dòng sau cho biết:

ak@HP2000:~$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> verbose = True
>>> def vprint(obj):
...     if verbose:
...         print(obj)
...     return
... 
>>> vprint('Norm and I')
Norm and I
>>> verbose = False
>>> vprint('I and Norm')
>>> 

Biến toàn cục "verbose" cũng có thể được đặt từ danh sách tham số.

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.