在编写一个需要在执行前进行一些“完整性检查”的小应用程序时,作者遇到了一个问题。例如,检查某个路径是否可读/可写/存在。代码如下:
import logging
import os
import shutil
import sys
from paths import PATH
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger('sf.core.sanity')
def sanity_access(path, mode):
ret = os.access(path, mode)
logfunc = log.debug if ret else log.warning
loginfo = (os.access.__name__, path, mode, ret)
logfunc('%s('%s', %s)==%s' % loginfo)
return ret
def sanity_check(bool_func, true_func, false_func):
ret = bool_func()
(logfunc, execfunc) = (log.debug, true_func) if ret else \
(log.warning, false_func)
logfunc('exec: %s', execfunc.__name__)
execfunc()
def sanity_checks():
sanity_check(lambda: sanity_access(PATH['userhome'], os.F_OK), \
lambda: None, sys.exit)
作者的问题与 sanity_check 函数有关。此函数接受三个参数 (bool_func、true_func、false_func)。如果 bool_func (测试函数,返回布尔值) 失败,则执行 true_func,否则执行 false_func。
使用 lambda: None 有些乏味,因为例如,如果 sanity_access 返回 True,则执行 lambda: None,打印的输出将是:
DEBUG:sf.core.sanity:access('/home/nomemory', 0)==True
DEBUG:sf.core.sanity:exec: <lambda>
因此,日志中不会很清楚地显示执行了哪个函数。日志中只包含 。那么,有没有一个默认函数可以什么都不做,并且可以作为参数传递?有没有办法返回正在 lambda 中执行的第一个函数的名称?或者有没有办法在作为参数发送“无”时不记录“exec”?什么是非/空操作的函数等价物?
sanity_check(lambda: sanity_access(PATH['userhome'], os.F_OK), \
<do nothing, but show something more useful than <lambda>>, sys.exit)
另一个问题是,为什么 lambda: pass 而非 lambda: None 不起作用?
2、解决方案
以下是五个不同的答案,每个答案都提供了一种解决作者问题的方法:
答案一:
- 推荐使用 K 组合子。这是一个允许您创建没有参数的函数的技巧。
- 使用 K 组合子,您可以创建自己的名为 none_function 的函数,该函数不执行任何操作。
- 然后,您可以将 none_function 作为参数传递给 sanity_check 函数。
答案二:
- 建议使用具有可选参数的函数。
- 这允许您为 ontrue 和 onfalse 参数指定默认值。
- 然后,您可以像这样调用 sanity_check 函数:
sanity_check(sanity_access(PATH['userhome'], os.F_OK), 'test home',
onfalse=sys.exit)
答案三:
- 建议使用可调用的对象。
- 这允许您创建可以像函数一样调用的类。
- 您可以创建自己的 SanityCheck 类,该类具有 check、ok 和 not_ok 方法。
- 然后,您可以创建 SanityCheck 的子类,这些子类具有自己的 check、ok 和 not_ok 方法。
- 您可以像这样调用 SanityCheck 类:
checks = ( PathSanityCheck(), AnotherPathSanityCheck() )
for c in checks:
c()
答案四:
- 解释了为什么 lambda: pass 失败。
- lambda 只允许表达式,而 pass 是一个语句。
答案五:
- 建议使用具有 name 属性的函数。
- 这允许您创建自己的函数,该函数不执行任何操作,但具有有用的 name 属性。
- 然后,您可以将此函数作为参数传递给 sanity_check 函数。