【问题标题】:Confusing ternary in - Python令人困惑的三元 - Python
【发布时间】:2013-08-23 20:15:07
【问题描述】:

假设我的列表只有strNone,并且我想检查将降低的字符串分配给另一个变量,但如果它是升高的或无,它应该呈现为无。

检查Nonestr.isupper() 的代码有效:

for i in [None,'foo',None,'FOO',None,'bar']:
  x = None if i is None or i.isupper() else i
  print x

但否定条件不起作用:

for i in [None,'foo',None,'FOO',None,'bar']:
  x = i if i is not None or i.isupper() else None
  print x

它返回AttributeError:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'NoneType' object has no attribute 'isupper'
  • 为什么会这样?
  • 除了循环中的三元赋值之外,还有其他方法可以执行相同的操作吗?

【问题讨论】:

  • 三元运算符使代码更难阅读

标签: python iteration conditional-statements ternary


【解决方案1】:

我喜欢 @Brionius 的 已编辑 答案,这是一种更通用的形式,从列表中返回项目的字典,由鉴别函数返回的值作为键(可以返回更多不只是对或错)。

from collections import defaultdict

def groupByEval(seq, fn):
    ret = defaultdict(list)
    for item in seq:
        ret[fn(item)].append(item)
    return dict(ret.iteritems())


test = [None,'foo',None,'FOO',None,'bar']
print groupByEval(test, lambda x: x is not None)
print groupByEval(test, lambda x: 0 if x is None else len(x))
print groupByEval(test, lambda x: x is not None and x.isupper())
print groupByEval(test, lambda x: x if x is None else sum(x.lower().count(c) for c in 'aeiou'))

给予:

{False: [None, None, None], True: ['foo', 'FOO', 'bar']}
{0: [None, None, None], 3: ['foo', 'FOO', 'bar']}
{False: [None, 'foo', None, None, 'bar'], True: ['FOO']}
{1: ['bar'], 2: ['foo', 'FOO'], None: [None, None, None]}

然后,Brionius 的解决方案可以实现为:

def splitItems(seq, condition):
    ret = groupByEval(seq, condition)
    return ret.get(True,[]), ret.get(False,[])

【讨论】:

    【解决方案2】:

    在返回AttributeError 的代码中,这是因为如果i is none,它会继续到or 语句的第二部分并检查i.isupper(),但None没有isupper() 方法,因此出现错误。

    我可以建议一种不同的方法吗?

    items = [None,'foo',None,'FOO',None,'bar']
    
    def splitItems(items, test):
        trueItems = []
        falseItems = []
        for item in items:
            if test(item):
                trueItems.append(item)
            else:
                falseItems.append(item)
        return trueItems, falseItems
    
    trueItems, falseItems = splitItems(items, lambda i:i is None or i.isupper())
    print "uppercase or None:", trueItems
    print "lowercase:", falseItems
    # uppercase or None: [None, None, 'FOO', None]
    # lowercase: ['foo', 'bar']
    

    【讨论】:

    • 从不对副作用使用条件表达式。
    • @MartijnPieters 你能解释一下你的意思吗?我不明白。
    • 条件表达式产生一个值,通常。但是您没有将它用于表达式结果。您正在使用它在两个列表之一上调用.append()。不要,只需在这种情况下使用普通的 if / else 构造。
    • 您正在使用表达式构造来产生副作用,这会导致混淆。
    • @MartijnPieters:好的 - 我似乎并不感到困惑 - 它看起来就像 if/else 语句的速记/句法糖。但我会听从你并改变它,无论哪种方式对我来说都很好。
    【解决方案3】:

    您正在测试i is not None or i.isupper()。当i None时,这不起作用:

    (i is not None) or (i.isupper())
    

    被懒惰地评估;首先评估左侧,并且仅当 False 是评估的第二个参数时。如果iNone,则第一个仅是False,因此右侧表达式仅被评估为None.isupper()

    改用and,并否定isupper()测试:

    x = i if i is not None and not i.isupper() else None
    

    上面的表达式是i is None or i.isupper()的正确逻辑反转。

    或者在您的原始表达式周围使用not (..)

    x = i if not (i is None or i.isupper()) else None
    

    因为你使用的是常规的for循环,所以这里不需要使用条件表达式;以下,分配给现有的循环变量i,就足够了:

    for i in [None,'foo',None,'FOO',None,'bar']:
          if i and i.isupper():
              i = None
          print i
    

    【讨论】:

      猜你喜欢
      • 2012-09-22
      • 2020-05-17
      • 1970-01-01
      • 2022-01-21
      • 2022-01-17
      • 2018-02-26
      • 1970-01-01
      • 1970-01-01
      • 2021-05-15
      相关资源
      最近更新 更多