วิธีการนำอ็อพชัน --verbose หรือ -v ไปใช้ในสคริปต์


95

ฉันรู้จัก--verboseหรือ-vจากเครื่องมือหลายอย่างและฉันต้องการนำสิ่งนี้ไปใช้ในสคริปต์และเครื่องมือของฉันเอง

ฉันคิดว่าจะวาง:

if verbose:
    print ...

ผ่านซอร์สโค้ดของฉันดังนั้นหากผู้ใช้ส่งผ่าน-vตัวเลือกตัวแปรverboseจะถูกตั้งค่าเป็นTrueและข้อความจะถูกพิมพ์

นี่เป็นแนวทางที่ถูกต้องหรือมีวิธีที่ธรรมดากว่านี้?

เพิ่มเติม: ฉันไม่ได้ขอวิธีใช้การแยกวิเคราะห์ข้อโต้แย้ง ที่ฉันรู้ว่ามันทำอย่างไร ฉันสนใจเป็นพิเศษในตัวเลือก verbose


9
เหตุใดจึงไม่ใช้โมดูลการบันทึกและตั้งค่าระดับการบันทึก INFO ตามค่าเริ่มต้นและดีบักเมื่อส่ง --verbose ดีที่สุดที่จะไม่นำสิ่งที่มีอยู่แล้วในภาษาไปใช้ซ้ำ ...
ทิม

3
@ ทิมฉันเห็นด้วย แต่โมดูลการบันทึกนั้นค่อนข้างเจ็บปวด
mlissner

คำตอบ:


109

คำแนะนำของฉันคือการใช้ฟังก์ชัน แต่แทนที่จะใส่ifฟังก์ชันซึ่งคุณอาจถูกล่อลวงให้ทำเช่นนี้:

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

(ใช่คุณสามารถกำหนดฟังก์ชันในifคำสั่งและจะกำหนดได้ก็ต่อเมื่อเงื่อนไขนั้นเป็นจริง!)

หากคุณใช้ Python 3 ซึ่งprintเป็นฟังก์ชันอยู่แล้ว (หรือหากคุณต้องการใช้printเป็นฟังก์ชันในการใช้ 2.x from __future__ import print_function) จะง่ายกว่า:

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

ด้วยวิธีนี้ฟังก์ชันจะถูกกำหนดให้เป็นแบบไม่ต้องทำอะไรเลยหากปิดโหมด verbose (โดยใช้แลมบ์ดา) แทนที่จะทดสอบverboseแฟล็กอย่างต่อเนื่อง

หากผู้ใช้สามารถเปลี่ยนโหมด verbosity ในระหว่างการรันโปรแกรมของคุณนี่อาจเป็นแนวทางที่ไม่ถูกต้อง (คุณต้องifใช้ฟังก์ชันนี้) แต่เนื่องจากคุณตั้งค่าด้วยแฟล็กบรรทัดคำสั่งคุณจึงต้อง ตัดสินใจครั้งเดียว

จากนั้นคุณจะใช้เช่นverboseprint("look at all my verbosity!", object(), 3)เมื่อใดก็ตามที่คุณต้องการพิมพ์ข้อความ "verbose"


1
ยิ่งไปกว่านั้นให้ทำตามprintหน้าที่: ยอมรับข้อโต้แย้งมากมาย สามารถใช้งานได้print(*args)ใน 3.x และfor arg in args: print arg,ใน 2.x ข้อได้เปรียบหลักคือช่วยให้สามารถผสมสตริงและสิ่งอื่น ๆ ในข้อความเดียวโดยไม่ต้องมีการstrโทร / การจัดรูปแบบและการเชื่อมต่อที่ชัดเจน

ที่ท้ายprint arg,บรรทัดใช้ลูกน้ำคืออะไร?
SamK

ซึ่งกำหนดได้ง่ายสำหรับตนเองโดยการทดลองหรือโดยการตรวจสอบเอกสาร แต่จะยับยั้งการแบ่งบรรทัดที่ปกติจะพิมพ์
kindall

5
ฟังก์ชันการพิมพ์ Python 3 ยังใช้อาร์กิวเมนต์คีย์เวิร์ดที่เป็นทางเลือกดังนั้นเพื่อสร้างฟังก์ชันการพิมพ์ใหม่อย่างสมบูรณ์:def verboseprint(*args, **kwargs): print(*args, **kwargs)
lstyls

63

ใช้loggingโมดูล:

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

ทั้งหมดนี้ไปที่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.

สำหรับข้อมูลเพิ่มเติมโปรดดูที่หลามเอกสารและบทเรียน


8
ตาม Python Docs ที่นี่ไม่ควรใช้การบันทึกในกรณีที่คุณต้องการพิมพ์เอาต์พุตในการทำงานปกติของโปรแกรมเท่านั้น ดูเหมือนว่านั่นคือสิ่งที่ OP ต้องการ
SANDeveloper

1
ดูเหมือนว่าจะดีสำหรับปัญหาพื้นฐาน แต่คำสั่ง * nix จำนวนมากยังรองรับการใช้คำฟุ่มเฟือยหลายระดับ (-v -v -v ฯลฯ ) ซึ่งอาจทำให้ยุ่งได้ด้วยวิธีนี้
TextGeek

12

การสร้างและทำให้คำตอบของ @ kindall ง่ายขึ้นนี่คือสิ่งที่ฉันมักจะใช้:

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

จากนั้นจะให้การใช้งานต่อไปนี้ตลอดทั้งสคริปต์ของคุณ:

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

และสคริปต์ของคุณสามารถเรียกได้ดังนี้:

% 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

หมายเหตุสองสาม:

  1. อาร์กิวเมนต์แรกของคุณคือระดับข้อผิดพลาดของคุณและอันดับที่สองคือข้อความของคุณ มันมีจำนวนวิเศษ3ที่กำหนดขอบเขตบนสำหรับการบันทึกของคุณ แต่ฉันยอมรับว่าเป็นการประนีประนอมเพื่อความเรียบง่าย
  2. หากคุณต้องการv_printทำงานตลอดทั้งโปรแกรมคุณต้องทำขยะกับคนทั่วโลก ไม่ใช่เรื่องสนุก แต่ฉันท้าทายใครสักคนเพื่อค้นหาวิธีที่ดีกว่า

1
ทำไมคุณไม่ใช้โมดูลการบันทึกสำหรับ INFO และ WARN นั่นคือนำเข้าเมื่อ-vถูกใช้ ในโซลูชันปัจจุบันของคุณทุกอย่างจะถูกถ่ายโอนไปยัง stdout แทนที่จะเป็น stderr และโดยปกติคุณต้องการส่งต่อทุกข้อผิดพลาดไปยังผู้ใช้ใช่หรือไม่?
Profpatsch

2
ใช่นั่นเป็นจุดที่ยุติธรรม การบันทึกมีค่าใช้จ่ายในการรับรู้บางอย่างที่ฉันพยายามหลีกเลี่ยง แต่มันอาจเป็นสิ่งที่ "ถูกต้อง" ที่ควรทำ มันทำให้ฉันรำคาญในอดีต ...
mlissner

9

สิ่งที่ฉันทำในสคริปต์ของฉันคือตรวจสอบที่รันไทม์ว่ามีการตั้งค่าตัวเลือก 'verbose' หรือไม่จากนั้นตั้งค่าระดับการบันทึกเป็นดีบัก หากยังไม่ได้ตั้งค่าฉันจะตั้งค่าเป็นข้อมูล วิธีนี้จะทำให้คุณไม่มี 'if verbose' ตรวจสอบโค้ดของคุณทั้งหมด


2

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


2

ฉันขโมยรหัสการบันทึกจากVirtualenvสำหรับโครงการของฉัน มองในmain()การvirtualenv.pyที่จะเห็นวิธีการที่จะเริ่มต้น รหัสที่โรยด้วยlogger.notify(), logger.info(), logger.warn()และไม่ชอบ ซึ่งวิธีการส่งออกจริงปล่อยจะถูกกำหนดโดยไม่ว่าจะ virtualenv ถูกเรียกด้วย-v, -vv, หรือ-vvv-q


2

โซลูชันของ @ kindall ใช้ไม่ได้กับ Python เวอร์ชัน 3.5 ของฉัน @styles ระบุอย่างถูกต้องในความคิดเห็นของเขาว่าเหตุผลคืออาร์กิวเมนต์คำหลักที่เป็นตัวเลือกเพิ่มเติม ดังนั้นเวอร์ชันที่ปรับปรุงเล็กน้อยสำหรับ Python 3 จึงมีลักษณะดังนี้:

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

1

อาจมีตัวแปรส่วนกลางซึ่งน่าจะตั้งค่าด้วยargparsefrom sys.argvซึ่งหมายถึงว่าโปรแกรมควรเป็นแบบละเอียดหรือไม่ จากนั้นมัณฑนากรสามารถเขียนได้เช่นนั้นหากเปิดใช้คำฟุ่มเฟือยอินพุตมาตรฐานจะถูกเปลี่ยนไปยังอุปกรณ์ว่างตราบใดที่ฟังก์ชันยังทำงาน:

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

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

ข้อเสียของการแก้ปัญหานี้คือการใช้คำฟุ่มเฟือยเป็นไบนารีซึ่งแตกต่างจากloggingที่ช่วยให้สามารถปรับแต่งวิธีการใช้โปรแกรม verbose ได้ละเอียดขึ้น นอกจากนี้การโทรทั้งหมด printจะถูกโอนสายซึ่งอาจไม่ต้องการ


0

สิ่งที่ฉันต้องการคือฟังก์ชันที่พิมพ์อ็อบเจกต์ (obj) แต่ถ้าตัวแปรโกลบอล verbose เป็นจริงมิฉะนั้นจะไม่ทำอะไรเลย

ฉันต้องการเปลี่ยนพารามิเตอร์โกลบอล "verbose" ได้ตลอดเวลา ความเรียบง่ายและความสามารถในการอ่านสำหรับฉันมีความสำคัญยิ่ง ดังนั้นฉันจะดำเนินการตามบรรทัดต่อไปนี้ระบุ:

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

ตัวแปรส่วนกลาง "verbose" สามารถตั้งค่าจากรายการพารามิเตอร์ได้เช่นกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.