我一直在想我是否应该写
lambda pixel: (0,0,0) if pixel == (0,0,0) else (255,255,255),
或
lambda pixel: pixel if pixel == (0,0,0) else (255,255,255),
这可能是一个愚蠢的问题,但我想更好地形成问题。
我想知道是否有任何我不知道的警告。
我个人认为后者更具可读性。
答案 0 :(得分:7)
绝对是后者。
如果您使用前者,并且有人只编辑其中一个文字值,那么它现在已经坏了:
lambda pixel: (0,0,0) if pixel == (0,0,8) else (255,255,255),
我在上面介绍了一个错误。我编辑了条件中使用的元组,但没有编辑第一个元组。通过随意检查很难发现这一点。
考虑一下:
big_long_expression if something == big_long_expression else whatever
读者每次都必须在心理上评估big_long_expression
,并比较它们是否实际相同。如果您改为使用后一种形式,现在很清楚:您要么保持表达式不变,要么在其他情况下返回其他形式。
这与我更喜欢+=
运算符的原因相同:
my_class.a.b[i+3] = my_class.a.b[i+3] + 2
与之比较:
my_class.a.b[i+3] += 2
两者都做同样的事情,但第二部分更容易理解。
我总是更喜欢更清楚地表达你意图的语法。我认为你的第二种格式最清楚:检查一个条件,当它真的保留值时,否则返回一个特殊值。
编辑:直接在这个问题的评论中,@ DSM提出了一个很好的观点。第一个表达式也有一个微妙的属性:它总是返回一个元组,即使被比较的表达式不是一个元组。只要pixel
是可以与元组进行比较的任何类,任何一个表达式都可以。但是如果你希望你的lambda始终从三元组的任何一个案例中返回一个元组,那么第一个形式更可取。
以下是您的代码的两次重写,供您考虑。
第一个:
ORIGIN = (0, 0, 0)
lambda pixel: ORIGIN if pixel == ORIGIN else (255,255,255),
现在至少有一个点可以编辑来改变原点。如果你想要总是返回一个元组,这就是我推荐的方式。
第二个:
lambda pixel: pixel if pixel == (0, 0, 0) else type(pixel)((255, 255, 255))
这将获得pixel
的类型,然后调用该类型来构造新对象。这假设pixel
是一个不仅与元组相当的类,而且如果你将一个元组传递给它,它将使用它来基于元组构建一个新的类实例。 / p>
使用中的一个例子:
f = lambda pixel: pixel if pixel == (0, 0, 0) else type(pixel)((255, 255, 255))
class T(tuple):
"""
Make a class that acts exactly like a tuple.
"""
pass
x = T((1,2,3))
assert type(x) == T
assert type(x) == type(f(x))
x = (1, 2, 3)
assert type(x) == tuple
assert type(x) == type(f(x))
答案 1 :(得分:3)
嗯,这肯定不是语法问题;这只是一种风格问题。 (在一个更复杂的表达中,它将是一个不同的故事......但是在一个更复杂的表达中,通常你应该把它分解成更简单的表达式......)
就个人而言,我认为它们都非常易读。第一个强调(0, 0, 0)
是更清楚的默认值,而第二个强调更清楚地回归的目的,并且它重复一个更简单的事情(pixel
而不是{{1 }})。但这些都是如此微不足道的好处,我认为这并不重要。