什么是可移植的方式 (python 2/python 3) 在提供默认答案的同时询问用户?

What would be a portable way (python 2/python 3) to interrogate the user while providing a default answer?

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

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

这样可以吗?

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

您可以使用 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 and an "easier to ask forgiveness than permission" 跨 Python 2.x 和 3.x 工作的方法。虽然这确实掩盖了 2.x 中的一个内置函数(有些人显然认为这是一种不好的做法),但无论如何你都不应该真正使用它(pylint 称它为糟糕的内置函数,事实上!)并使编写 3.x 风格的代码变得更容易,这些代码将在 2.x 中继续 运行。另一种方法是在这两种情况下都使用别名,例如:

try:
    get_input = raw_input
except NameError:
    get_input = input

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

如果问题的重点是与 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()。这允许更多 "native" python 3 代码,但如果您使用 python 2 规范阅读它会造成混淆。我想如果代码 一致 在 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