2016-02-11 14 views
7

私はしばしばユーザーに入力を求められます。私はいつも私の主な実行スクリプトに "必要に応じて"私のプロンプトを書いています。これは一種の醜いです。複数のスクリプトで同じタイプの入力を頻繁に要求するため、私のコードの大部分は単にコピー/ペーストされたプロンプトループです。ここで私が過去にやったものだ:きれいで、柔軟で、維持しやすいユーザー入力プロンプトの作成

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 

私は次のように呼び出すことができるものを作成したいと思います:

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 

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

その後プロンプト機能は次のようになります

私が検討している代替案は、プロンプトクラスを作成することです。これにより、結果に柔軟性を持たせることができます。たとえば、 "y"または "n"をTrueまたはFalseに変換する場合、上記は実際には機能しません。

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

私が検討しているもう1つの方法は、個別のプロンプトを作成し、それぞれの名前をオリジナルのコードに似た名前にすることです。これは実際に何も変更しません。

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 

私はそれが受け入れられた1「Y/N」形式に落ち着くためにおそらくちょうど良いアイデアだことを実現:それはちょうど簡単にメインの実行コードが閲覧できるようになり、私のメインの実行コードのうち、これらのループを取得するのに役立ちます私のすべてのプログラムのために、私はします。これは、非常に似ているがわずかに異なるプロンプトが必要な場合、コピー/貼り付けされたコードがたくさんあることになります(この方法では柔軟性がないことを示すためです)。

清潔で柔軟性があり、保守しやすいユーザーのプロンプトを書くには、どのような方法が適していますか?

(私はこれを見ました:Asking the user for input until they give a valid responseと他のいくつかの回答です。私の質問は、入力を取得して検証する方法ではなく、複数のプログラム間で再利用できる柔軟な入力システムを作る方法です)。

+3

私はいつも私のユースケースは、困難な汎用ソリューションを書く作るのに十分カスタマイズする必要があったことを発見しました。私は誰もが実用的な解決策が、良い質問が出てくるかどうかを見て興奮しています。 –

+0

柔軟で再利用可能なプロンプトシステムを設計することは、Stack Overflowの対象外です。 – chepner

+0

@chepnerまあ、それは私の質問のようなものです!それは本当に「設計」されていなければならないものですか(その場合私は同意し、SOの回答全体を投稿することはできません)、これは私の質問です。これは私がちょうど考えていない清潔でシンプルなものですか?私が集めていることから、これは本当に複雑な解決策を必要とする質問であるという答えです。前者の場合でも、疑問は依然として私が考えるように、同様の質問をした他の人々に、実際には1つの投稿で答えるだけでは簡単ではないことを示すために役立ちます。 – CptSupermrkt

答えて

0

私はかつて同じような機能を書いていました。説明はDOC-文字列である:

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 

例:

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

私は非常に明確に定義されたビルディング・ブロックの数を含むライブラリ、など小型軽量それぞれ1を書くことをお勧めします可能な限り重量を増やします。どのようにして一緒に置くかについてはあまりにも多くの前提がありません。

つまり、ループを実行する関数を1つ含めるのではなく、一連のルールを渡す代わりに、正確に1つの関数を渡すことができます。入力が不可能な場合はValueErrorとします)。他のビルディングブロックは、特定のチェックまたは変換(ブール値に'y''n'の解決のような)を実装します。

このようにして、ユースケースに適した方法で組み立てることができます。

# 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) 
関連する問題