【问题标题】:Is avoiding declaring a variable as None all that bad?避免将变量声明为 None 有那么糟糕吗?
【发布时间】:2014-11-05 01:41:27
【问题描述】:

我正在研究 Python 中的一个经典学生问题,在一系列条目中查找最大值和最小值。通常,我想通过将所有有效的用户条目放在一个列表中来做到这一点,但是我们应该一个一个地评估每个条目,因为它来自用户。 (在这个类中,我们在列表之前覆盖循环。)

由于我没有使用列表,因此我不愿将变量声明为 None,然后评估“最大为 None 或最大

所以问题来了:这是对 try/except 的有效使用吗?它比注释的替代方案更不pythonic吗? (当然,最 Python 的方式是 min([list]) 但我在这里。)

#smallest = None

while True:
    num = raw_input("Enter a number: ")
    if num == "done" : break
    try:
        num = int(num)
    except:
        print 'Invalid input'
        continue

    try:
        smallest = min(num,smallest)
    except NameError:
        smallest = num

#    if num < smallest or smallest is None:
#        smallest = num    

print "Minimum is", smallest

【问题讨论】:

  • 规则:异常应该是exceptional,不要覆盖编码;只有少数情况下使用它们比首先执行检查更适用(并且请在任何使用点之前绑定局部变量!)
  • 首先,如果您在谈论“声明变量”,这通常表明您没有正确考虑 Python。从技术上讲,您可以说变量是通过在当前编译单元中的任何地方使用的名称来“声明”的,但这更多的是误导而不是有用。
  • @user2864740:在 this 的情况下,这至少是有争议的,但总的来说,这根本不是真的,它直接违背了 Python 中的基本 EAFP 原则。
  • @abarnert 我认为 EAFP(尤其是带有字典键的)更像是一种可接受的特殊情况,就像试图将字符串强制转换为整数一样。 (在许多情况下,无效输入确实属于“异常”的情况。)同样,它不应该覆盖代码。
  • “例外应该是处理例外情况” - 应该有人告诉StopIterationGeneratorExit

标签: python nonetype try-except


【解决方案1】:

不,try/except 的用法非常可笑。

预先声明并定义为None,然后更新为

smallest = num if smallest is None else min(num, smallest)

或者,您可以将其初始化为smallest = [],然后将其更新为

smallest = [min(smallest + [num])]

然后结束它

print("Minimum is {}".format(smallest) if smallest else "No numbers!")

【讨论】:

  • +1 表示第一个选项。正如 OP 所说,他们不允许使用列表,第二个答案不好。
  • 我认为这意味着他们应该避免将整个数字集合存储在内存中,而不是因为任何原因他们不应该使用列表。但如果你真的想避免使用第二个选项list,你可以简单地换成tupleset
  • @ChrisMartin:我认为使用集合的最后一个选项,因为他们不允许使用列表是行不通的……
  • @ChrisMartin:我同意,问题中的实际措辞是“相反,我们应该逐个评估来自用户的每个条目”,并评估“两个两个”正如您所做的那样,而不是“一个一个”不是问题;它仍然是 O(1) 而不是 O(N),我认为这是老师最终追求的(即使他显然不会以这种方式向学生解释)。只是您评论的后半部分我认为没有添加任何内容;我无法想象任何老师会因为使用list 而让你失望,但会因为使用set 而让你失望……
  • 更多的是思想实验而不是实际建议。
【解决方案2】:

虽然尝试和处理异常通常是 Python 的基本原则(EAFP, or Easier to Ask Forgiveness than Permission),但它并不总是合适。

至少,NameError 是您的代码中需要修复的逻辑错误,而不是需要处理的异常情况,因此这里有一种非常糟糕的“代码气味” .

另外,考虑如果用户从不输入任何数字会发生什么。如果你初始化了smallest = None,它会打印出Minimum is None,这也不算太不合理。如果您未对其进行初始化,它将从未处理的NameError 中打印出堆栈跟踪,这对用户来说不太友好。


但有一个简单的替代方案:只需选择一个大于任何有效输入的起始值,然后您就不需要检查(无论是通过 EAFP 还是 LBYL):smallest = min(num, smallest) 始终是正确答案。

你可以为此使用什么价值?好吧,无穷大显然比任何整数都大,所以你可以从float('inf')开始。

(我不确定Minimum is inf 在无值情况下是否比Minimum is None 更好或更差,但它肯定比回溯更好。)


同时,值得注意的是,有一个更简洁的设计可以从一开始就避免这个问题。只需分解您的代码以生成整数流,然后您就可以对生成的可迭代对象做任何您想做的事情。例如:

def numbers():
    while True:
        num = raw_input("Enter a number: ")
        if num == "done" : break
        try:
            yield int(num)
        except:
            print 'Invalid input'

smallest = min(numbers())

这具有构建列表的所有优点(您可以在其上调用min)而没有缺点(您不必实际在内存中构建列表)。

您可以进一步简化这一过程,方法是使用raw_input 周围的两个参数iter 来生成字符串生成器,然后分两步将其转换为整数生成器,然后将其传递给min。不过我觉得这种方式对于新手来说比较容易理解。

【讨论】:

  • 如果他的班级还没有完成列表,我怀疑他们已经完成了函数,更不用说迭代器了。
  • @whereswalden:是的,但是他写try:/except NameError: 的事实意味着他在课堂上已经非常努力了,所以……
  • @whereswalden:另外,OP 自己清楚地了解列表,他不能在这里使用列表的原因不是他被告知不允许使用,而是“我们应该逐一评估每个条目,因为它来自用户”。发电机可以做什么。当然,他必须了解它是如何工作的,并且能够向他的老师解释,但他看起来是个聪明人。
  • @Rache:不,inf 是语言内置的。它没有内置符号,但您可以使用float('inf') 创建它(以及其他方式),正如答案中所示。 (嗯,从技术上讲,至少在 2.7 中,我认为它只保证存在于 C double 类型是 IEEE 浮点类型的平台上,但实际上,你可以指望它。如果你需要语言真正保证的东西,总是有decimal.Decimal('inf')。)
  • @pbhj:很好的洞察力——这正是迭代器的用途。如果你想要一些你可以像列表一样迭代的东西,但没有理由在列表上浪费内存(并且只需要迭代一次),它们是两全其美的。但我把它放在我回答的最后,因为这可能不是学习生成器的合适时间,对于那些仅仅通过学习列表就已经领先于他的班级的人......
猜你喜欢
  • 2014-12-13
  • 1970-01-01
  • 2013-01-15
  • 2015-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多