Air*_*Air 8 python design-patterns
有时我发现自己编写这样的代码:
def analyse(somedata):
result = bestapproach(somedata)
if result:
return result
else:
result = notasgood(somedata)
if result:
return result
else:
result = betterthannothing(somedata)
if result:
return result
else:
return None
Run Code Online (Sandbox Code Playgroud)
那太难看了.当然,有些人喜欢省略一些语法:
def analyse(somedata):
result = bestapproach(somedata)
if result:
return result
result = notasgood(somedata)
if result:
return result
result = betterthannothing(somedata)
if result:
return result
Run Code Online (Sandbox Code Playgroud)
但这并没有太大改善; 这里仍然有大量重复的代码,它仍然很难看.
我研究了使用iter()带有sentinel值的内置函数,但是在这种情况下,该None值用于表示循环应该继续运行,而不是用于表示循环应该终止的标记.
在Python中是否有任何其他(理智)技术用于实现这种"继续尝试,直到找到可行的方式"模式?
我应该澄清"返回值满足某些条件"不仅限于条件if bool(result) is True如示例中的情况.可能的是,可能的分析函数列表各自产生一些衡量成功程度的系数(例如,R平方值),并且您希望设置接受的最小阈值.因此,一般的解决方案不应该固有地依赖于结果的真值.
or当总函数的数量是a)已知,并且b)小,并且测试条件完全基于返回的真值时,可以简单地使用orGrapsus建议:
d = 'somedata'
result = f1(d) or f2(d) or f3(d) or f4(d)
Run Code Online (Sandbox Code Playgroud)
因为Python的布尔运算符短路,所以函数从右到左执行,直到其中一个产生一个被评估为的返回值True,此时进行赋值result并且不评估其余的函数; 或直到你用完了功能,并被result分配False.
当总函数的数量为a)未知,或b)非常大时,单行生成器理解方法起作用,如Bitwise建议:
result = (r for r in (f(somedata) for f in functions) if <test-condition>).next()
Run Code Online (Sandbox Code Playgroud)
与选项#1相比,这具有额外的优势,您可以使用任何<test-condition>您想要的,而不是仅仅依赖于真值.每次.next()都叫:
ffrom functions并尝试评估f(somedata)f是一个函数并且somedata是一个有效的arugment),内部生成器会产生f(somedata)外部生成器的返回值<test-condition>满足,则外部生成器生成返回值,f(somedata)并将其指定为result<test-condition>在步骤5中不满意,请重复步骤2-4这种方法的一个缺点是嵌套的理解可能不如它们的多行等价物直观.此外,如果内部发生器耗尽而没有满足测试条件,则.next()引发StopIteration必须处理的(在try-except块中)或阻止(通过确保最后一个函数将始终"成功").
由于我们可以将可调用函数放在列表中,因此一个选项是按照应该使用的顺序显式列出要"尝试"的函数,然后遍历该列表:
def analyse(somedata):
analysis_functions = [best, okay, poor]
for f in analysis_functions:
result = f(somedata)
if result:
return result
Run Code Online (Sandbox Code Playgroud)
优点:修复了重复代码的问题,更清楚的是你正在进行迭代过程,并且它会短路(在找到"好"结果后不会继续执行函数).
这也可以用Python的for ... else语法编写:*
def analyse(somedata):
analysis_functions = [best, okay, poor]
for f in analysis_functions:
result = f(somedata)
if result:
break
else:
return None
return result
Run Code Online (Sandbox Code Playgroud)
这里的优点是可以识别退出函数的不同方法,如果您希望analyse()函数完全失败以返回其他内容None或引发异常,这可能很有用.否则,它会更长,更深奥.
*如"将代码转换为美丽的,惯用的Python"中所述,从15:50开始.
如果功能数量不是太高,为什么不使用or运算符?
d = 'somedata'
result = f1(d) or f2(d) or f3(d) or f4(d)
Run Code Online (Sandbox Code Playgroud)
它只会应用这些函数,直到其中一个函数返回不是False.