2016-02-11 15 views
7

Tôi thường được giao nhiệm vụ yêu cầu người dùng nhập liệu. Tôi đã luôn luôn viết lời nhắc của tôi "là cần thiết" trong các kịch bản thực thi chính của tôi. Đây là loại xấu xí, và bởi vì tôi thường yêu cầu cùng một kiểu đầu vào trên nhiều tập lệnh, một tấn mã của tôi chỉ là các vòng lặp được sao chép/dán. Dưới đây là những gì tôi đã làm trong quá khứ:Viết lời nhắc nhập người dùng sạch, linh hoạt và dễ bảo trì

while True: 
    username = input("Enter New Username: ") 
    if ldap.search(username): 
     print " [!] Username already taken." 
    if not validator.validate_username(username): 
     print " [!] Invalid Username." 
    else: 
     break 

tôi muốn tạo một cái gì đó có thể được gọi như:

username = prompt(prompt="Enter New Username: ", 
        default=None, 
        rules=["user_does_not_exist", 
         "valid_username"]) 

Sau đó, chức năng nhanh chóng trông giống như:

def prompt(prompt, default, rules): 
    while True: 
     retval = input(prompt) 
     if default and retval == "": 
      break 
      return default 
     if not rule_match(retval, rules): 
      continue 
     break 
     return retval 

def rule_match(value, rules): 
    if "user_does_not_exist" in rules: 
     if not user.user_exists(value): 
      return False 

    if "valid_username" in rules: 
     if not validator.username(value): 
      return False 

    if "y_n_or_yes_no" in rules: 
     if "ignore_case" in rules: 
      if value.lower() not in ["y", "yes", "n", "no"]: 
       return False 
     else: 
      if value not in ["y", "yes", "n", "no"]: 
       return False 

    return True 

Một giải pháp thay thế mà tôi đang cân nhắc là tạo một lớp Prompt, điều này sẽ cho phép sự linh hoạt hơn với kết quả. Ví dụ: nếu tôi muốn chuyển đổi "y" hoặc "n" thành True hoặc False, thì điều trên không thực sự hiệu quả.

create_another = Prompt(prompt="Create another user? (y/n): ," 
         default=False, 
         rules=["y_n_or_yes_no", 
           "ignore_case"]).prompt().convert_to_bool() 

Cách thay thế khác mà tôi đang xem xét chỉ là tạo lời nhắc riêng và đặt tên cho chúng, với mỗi mã được viết giống với mã rất ban đầu của tôi. Điều này không thực sự thay đổi bất cứ điều gì. Nó chỉ phục vụ để có được những vòng ra mã thực thi chính của tôi mà làm cho mã thực thi chính dễ dàng hơn để duyệt:

username = prompt("get_new_username") 

def prompt(prompt_name): 
    if prompt_name == "get_new_username": 
     while True: 
      username = input("Enter New Username: ") 
      if ldap.search(username): 
       print " [!] Username already taken." 
      if not validator.validate_username(username): 
       print " [!] Invalid Username." 
      else: 
       break 
       return username 

    if prompt_name == "y_n_yes_no_ignore_case": 
     # do prompt 
    if prompt_name == "y_n_yes_no": 
     # do prompt 
    if prompt_name == "y_n": 
     # do prompt 
    if prompt_name == "y_n_ignore_case": 
     # do prompt 
    if prompt_name == "yes_no": 
     # do prompt 
    if prompt_name == "yes_no_ignore_case": 
     # do prompt 

Tôi nhận ra rằng nó có thể chỉ là một ý tưởng tốt để giải quyết trên một chấp nhận "/ y n" định dạng cho tất cả các chương trình của tôi, và tôi sẽ. Đây chỉ là để cho thấy rằng, trong trường hợp tôi sẽ cần một dấu nhắc rất giống nhau nhưng hơi khác nhau, nó sẽ dẫn đến rất nhiều mã sao chép/dán (không có sự linh hoạt với phương pháp này cả).

Cách tiếp cận tốt để viết lời nhắc người dùng sạch, linh hoạt và dễ duy trì là gì?

(Tôi đã thấy điều này: Asking the user for input until they give a valid response và một số câu trả lời khác. Câu hỏi của tôi không phải là về cách nhập và xác thực, đó là cách tạo hệ thống nhập linh hoạt có thể được sử dụng lại với nhiều chương trình).

+3

Tôi luôn thấy rằng các trường hợp sử dụng của mình cần phải được tùy chỉnh đủ để làm cho việc viết một giải pháp mục đích chung khó khăn. Tôi rất vui khi thấy có ai đưa ra giải pháp thực tế hay không, câu hỏi hay. –

+0

Thiết kế hệ thống nhắc nhở linh hoạt, có thể tái sử dụng hơi vượt quá phạm vi của Stack Overflow. – chepner

+0

@chepner Vâng, đó là loại câu hỏi của tôi! Có phải thứ gì đó phải được thiết kế "chuyên sâu" (trong trường hợp này tôi đồng ý, chúng tôi không thể đăng toàn bộ chương trình trong câu trả lời SO), hoặc, và đây là câu hỏi của tôi, có giải pháp hay cách nào đó để điều này là sạch sẽ và đơn giản mà tôi chỉ không nghĩ đến? Từ những gì tôi thu thập, câu trả lời là đây thực sự là một câu hỏi đòi hỏi một giải pháp phức tạp. Ngay cả trong trường hợp của cựu, câu hỏi vẫn còn phục vụ công đức tôi nghĩ, để hiển thị những người khác với những câu hỏi tương tự mà thực sự nó không phải là đơn giản, đủ để trả lời trong một bài viết. – CptSupermrkt

Trả lời

0

Tôi đã từng viết một hàm cho một mục tương tự. Lời giải thích là trong doc-chuỗi:

def xory(question = "", setx = ["yes"], sety = ["no"], setz = [], strict = False): 
    """xory([question][, setx][, sety][, setz][, strict]) -> string 

    Asks question. If the answer is equal to one of the elements in setx, 
    returns True. If the answer is equal to one of the elements in sety, 
    returns False. If the answer is equal to one of the elements in setz, 
    returns the element in setz that answer is equal to. If the answer is 
    not in any of the sets, reasks the question. Strict controls whether 
    the answer is case-sensitive. If show is True, an indication of the 
    acceptable answers will be displayed next to the prompt.""" 

    if isinstance(setx, str): 
     setx = [setx] 
    if isinstance(sety, str): 
     sety = [sety] 
    if isinstance(setz, str): 
     setz = [setz] 
    if (setx[0])[0] != (sety[0])[0]: 
     setx = [(setx[0])[0]] + setx 
     sety = [(sety[0])[0]] + sety 
    question = question.strip(" ") + " " 
    while True: 
     if show: 
      shows = "[%s/%s] " % (setx[0], sety[0]) 
     else: 
      shows = "" 
     user_input = raw_input(question + shows) 
     for y in [setx, sety, setz]: 
      for x in y: 
       if (user_input == x) or ((not strict) and (user_input.lower() == x.lower())): 
        if y is setx: 
         return True 
        elif y is sety: 
         return False 
        else: 
         return x 
     question = "" 
     show = True 

Ví dụ:

>>> response = xory("1 or 0?", ["1", "one", "uno"], ["0", "zero", "null"], ["quit", "exit"]) 
1 or 0? x 
[1/0] eante 
[1/0] uno 
>>> print(response) 
True 
>>> response = xory("Is that so?") 
Is that so? Who knows? 
[y/n] no 
>>> print(response) 
False 
>>> response = xory("Will you do it?", setz=["quit", "exit", "restart"]) 
Will you do it? hm 
[y/n] quit 
>>> print(response) 
quit 
0

Tôi muốn tư vấn để viết một thư viện có chứa một số khối xây dựng rất rõ ràng, mỗi người càng nhỏ và ánh sáng -trọng lượng càng tốt, mà không có quá nhiều giả định nướng về cách bạn sẽ ghép các mảnh lại với nhau.

Tức là, tôi sẽ bao gồm một hàm thực hiện vòng lặp, nhưng thay vì truyền vào một bộ quy tắc, tôi cho phép chính xác một hàm được truyền trong đó trả về một giá trị (nếu đầu vào hợp lệ đã được đưa ra và sau khi chuyển đổi nó theo bất kỳ cách nào cần thiết) hoặc tăng một ValueError nếu đầu vào không thể sử dụng được. Các khối xây dựng khác sẽ thực hiện kiểm tra hoặc biến đổi nhất định (như độ phân giải 'y''n' thành giá trị boolean).

Bằng cách này, bạn hoàn toàn có thể để người dùng lắp ráp nội dung theo cách phù hợp với trường hợp sử dụng.

# library: 

def prompt(prompt, default, postprocess): 
    value = input('{} ({}): '.format(prompt, default)) or default 
    try: 
     return postprocess(value) 
    except ValueError: 
     continue 

def check_lower(value): 
    if not value.islower(): 
     raise ValueError() 

def to_bool(value): 
    return value in 'yes' 

# using the library: 

def postprocess(value): 
    check_lower(value) 
    return to_bool(value) 

prompt('Really?', 'n', postprocess) 
Các vấn đề liên quan