将'None'作为函数参数传递(参数为函数)

8 投票
5 回答
25393 浏览
提问于 2025-04-16 04:15

我正在写一个小应用程序,它在执行之前需要进行一些“合理性检查”。(比如,合理性检查的例子:测试某个路径是否可读、可写或是否存在)

代码如下:

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_functrue_funcfalse_func)。如果 bool_func(这是一个测试函数,会返回一个布尔值)失败了,就会执行 true_func,否则就执行 false_func

1) lambda: None 有点无趣,因为比如说如果合理性检查返回 True,那么 lambda: None 就会被执行,输出的内容会是:

DEBUG:sf.core.sanity:access('/home/nomemory', 0)==True
DEBUG:sf.core.sanity:exec: <lambda>

这样在日志中就不太清楚到底执行了哪个函数。日志里只会显示 <lambda>。有没有什么默认的函数可以什么都不做,并且可以作为参数传入?有没有办法在 lambda 里返回第一个被执行的函数的名字?

或者有没有办法在传入“什么都不做”的参数时,不记录“执行”的日志?

对于函数来说,什么是“什么都不做”的等价物?

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 不起作用?

5 个回答

3

你可能需要重新考虑一下这个问题。

class SanityCheck( object ):
    def __call__( self ):
        if self.check():
            logger.debug(...)
            self.ok()
        else:
            logger.warning(...)
            self.not_ok()
    def check( self ):
        return True
    def ok( self ):
        pass
    def not_ok( self ):
        sys.exit(1)

class PathSanityCheck(SanityCheck):
    path = "/path/to/resource"
    def check( self ):
        return os.access( path, os.F_OK )

class AnotherPathSanityCheck(SanityCheck):
    path = "/another/path"

def startup():
    checks = ( PathSanityCheck(), AnotherPathSanityCheck() )
    for c in checks:
        c()

可调用对象可以让你的生活更简单。

8

更新

我本来想删掉这篇帖子,因为THC4k看透了所有复杂的东西,并正确地重写了你的函数。不过在不同的情况下,K组合子这个技巧可能会派上用场,所以我就留着它了。


据我所知,没有内置的功能可以做到你想要的。我觉得你想要的是K组合子(这个链接在另一个问题中提到过),它可以用以下方式表示:

 def K_combinator(x, name):
     def f():
         return x
     f.__name__ = name
     return f

 none_function = K_combinator(None, 'none_function')

 print none_function()

当然,如果这只是一次性的需求,你可以直接这样做:

def none_function():
    return None

但那样你就不能说“K组合子”了。使用'K组合子'的另一个好处是你可以把它传递给函数,比如:

foo(call_back1, K_combinator(None, 'name_for_logging'))

至于你的第二个说法,lambda中只允许表达式。pass是一个语句,所以lambda: pass会失败。

你可以稍微简化一下对sanity check的调用,去掉第一个参数周围的lambda。

def sanity_check(b, true_func, false_func):
    if b:
        logfunc = log.debug
        execfunc = true_func
    else:
        logfunc = log.warning
        execfunc = false_func
    logfunc('exec: %s', execfunc.__name__)
    execfunc()

def sanity_checks():
    sanity_check(sanity_access(PATH['userhome'], os.F_OK),
                 K_combinator(None, 'none_func'), sys.exit)

这样更容易理解(主要是把三元运算符展开成if语句)。boolfunc没有起作用,因为sanity_check没有给调用添加任何参数。与其把它包裹在lambda中,不如直接调用。

8

这些没什么用的lambda表达式是怎么回事呢?其实,可能可选参数能帮到你一些:

def sanity_check( test, name='undefined', ontrue=None, onfalse=None ):
    if test:
        log.debug(name)
        if ontrue is not None:
            ontrue()
    else:
        log.warn( name )
        if onfalse is not None:
            onfalse()

def sanity_checks():
    sanity_check(sanity_access(PATH['userhome'], os.F_OK), 'test home', 
        onfalse=sys.exit)

不过,你真的把事情搞得太复杂了。

撰写回答