【问题标题】:What would be a portable way (python 2/python 3) to interrogate the user while providing a default answer?在提供默认答案的同时询问用户的可移植方式(python 2/python 3)是什么?
【发布时间】:2015-11-03 11:24:09
【问题描述】:

我想问用户一个有默认答案的问题,例如:

How many apples do you want? [default 40]: 50
You have requested 50 apples.

如何以简洁、健壮的方式对它进行编码,以便在 Python 2 和 3 中使用?如何使默认值的显示方式看起来像上面显示的那样?

我有以下尝试的开始:

def interrogate_with_default(
    prompt  = None,
    default = None
    ):
    readline.set_startup_hook(lambda: readline.insert_text(default))
    #readline.set_startup_hook(lambda: readline.insert_text(
    #    " [default: {default}]: ".format(
    #        default = default
    #    )
    #))
    try:
        return get_input(prompt)
    finally:
        readline.set_startup_hook()

def get_input(
    prompt = None
    ):
    if sys.version_info >= (3, 0):
        return input(prompt)
    else:
        return raw_input(prompt)


number_of_apples = interrogate_with_default(
    prompt  = "How many apples do you want?",
    default = "40"
)

【问题讨论】:

    标签: python python-3.x input default python-2.x


    【解决方案1】:

    如果问题的重点是与 python 2 和 python 3 的兼容性,答案是:不要以临时的方式进行!有很多事情需要注意。如果您无法避免必须支持多个平台,请选择为此目的而设计的库并始终如一地使用它。我相信six 是当前最先进的技术:

    from __future__ import print_function
    import six
    

    然后使用six.moves.input() 读取用户输入。它在 python 2 上绑定到 raw_input,在 python 3 上绑定到常规 input。所以基本上你将编写一个 python 3 程序,在模块顶部带有兼容性管道。

    替代方案:你可以写成from six.moves import input,它在python 2上使得input()表现得像raw_input()。这允许更多“本机”python 3 代码,但如果您使用 python 2 规范阅读它会令人困惑。我想如果代码 consistently 在 python 3 样式中是安全的。

    对于默认设置部分,请保持明确,以便您可以轻松理解和调试它。我喜欢提前设置默认值。然后我会简单地阅读一个答案,如果它不为空(且有效)则使用它

    apples = 40
    ...
    response = six.moves.input("How many apples do you want? [%d]: " % apples).strip()
    if response:
        apples = int(response)
    

    与建议的 input("...") or 40 技巧相比,此设置的优势在于您可以进一步验证并忽略负值、小数值、键盘混搭,以及通常不适合您特定问题的任何内容正在问(生活中比非负整数更多)。

    在这种情况下,您可以使用 try-except 块轻松丢弃非整数值:

    if response:
        try:
            apples = int(response)
        except ValueError:
            pass
    

    【讨论】:

    • ...但是 from six import input 在 2.x 上影响 input!与自己动手相比,这或多或少会让读者感到困惑?
    • 是的,我同意。我根据您的回答对我的反对意见进行了限定,因为这 OP 目标的一部分。但我仍在考虑我的答案;我认为使用six.input() 会更干净。
    • 无论如何,如果代码始终以 python 3 风格编写,并带有一个使 python 2 适应它的标头,那么混淆的可能性就会较小。
    • @jonrsharpe 好的,现在问题变成了:我怎样才能使用一个函数来获取同时适用于 Python 2 和 Python 3 的输入数据? :P跨度>
    • 这似乎最近发生了变化。在six1.10.0 我需要使用six.moves.input 而不是six.input,同样from six.moves import input
    【解决方案2】:

    您可以使用or 为用户不输入任何内容的情况提供默认值(因为结果是一个空字符串,evaluates false-y):

    >>> 'foo' or 'bar'
    'foo'
    >>> '' or 'bar'
    'bar'
    

    要将其集成到您可以实际使用的函数中(!r 用于调用 __repr__,例如,字符串包含引号):

    try:
        input = raw_input
    except NameError:
        pass
    
    def interrogate(prompt, default=None):
        """Ask the user to enter a value (or accept optional default)."""
        if default is None:
            return input('{}: '.format(prompt))
        return input('{} [default {!r}]: '.format(prompt, default)) or default
    

    注意遵守the style guide"easier to ask forgiveness than permission" 跨 Python 2.x 和 3.x 工作的方法。尽管这确实影响了 2.x 中的内置函数(有些人显然认为这是不好的做法),但无论如何你都不应该真正使用它(pylint 称它是一个糟糕的内置函数,事实上!)并且更容易编写将继续在 2.x 中运行的 3.x 风格的代码。另一种方法是在这两种情况下都使用别名,例如:

    try:
        get_input = raw_input
    except NameError:
        get_input = input
    

    然后在任何地方使用get_input;如果您将 2.x 和 3.x 视为同等重要,这可能更合适。

    【讨论】:

    • 我不认为input = raw_input 是个好主意...(P.S. 不是我的反对意见)
    • 当你不确定的时候责备任何人不是很愚蠢吗?
    • 因为我怎样才能再次调用默认的input()函数?
    • @KevinGuan 你为什么要这样做?如果您确实想要评估输入,这通常不是一个好主意,您仍然可以使用eval(input(...)),由于阴影,它在 2.x 和 3.x 中将继续工作。
    • 对 python 内置函数的含义进行临时更改是一个坏主意。它使阅读代码的其他人感到困惑,并且在您再次阅读代码时使您感到困惑。如果您使用的是 python 2 并且想要原始输入,请使用 raw_input()
    【解决方案3】:

    这样好吗?

    my_input = int(input("How many apples do you want? [default 40]: ") or "40")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-24
      • 1970-01-01
      • 2020-02-15
      • 1970-01-01
      • 2019-12-16
      • 2022-07-26
      • 1970-01-01
      • 2023-03-26
      相关资源
      最近更新 更多