Передача None в качестве параметра функции (где параметр является функцией) - PullRequest
8 голосов
/ 17 сентября 2010

Я пишу небольшое приложение, которое должно выполнить некоторые проверки работоспособности перед тем, как войти в исполнение.(например, проверки работоспособности: проверьте, доступен ли какой-либо путь для чтения / записи / существует)

Код:

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.

Эта функция принимает 3 параметра (bool_func, true_func, false_func).Если bool_func (который является тестовой функцией, возвращающей логическое значение) завершается неудачно, true_func выполняется, в противном случае false_func выполняется.

1) 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 не работает?

Ответы [ 5 ]

8 голосов
/ 17 сентября 2010

Что со всеми лямбдами, которые не служат цели?Ну, может быть, вам помогут дополнительные аргументы:

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)

Но вы действительно усложняете вещи.

8 голосов
/ 17 сентября 2010

update

Обычно я удаляю этот пост, потому что THC4k разобрался со всей сложностью и правильно переписал вашу функцию.Однако в другом контексте трюк с комбинатором K может пригодиться, поэтому я оставлю его.


Нет встроенной функции, которая бы выполняла то, что вы хотите AFIK.Я полагаю, что вам нужен комбинатор 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()

, конечно, если это всего лишь один, то вы можете простоdo

def none_function():
    return None

Но тогда вы не скажете "K комбинатор".Другое преимущество подхода 'K_combinator' состоит в том, что вы можете передавать его функциям, например,

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

, поскольку для вашего второго оператора в lambda допускаются только выражения.pass это утверждение.Следовательно, lambda: pass терпит неудачу.

Вы можете немного упростить проверку призыва к здравомыслию, удалив лямбду вокруг первого аргумента.

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 не добавлял никаких аргументов к вызову.Можно просто позвонить, а не оборачивать в лямбду.

3 голосов
/ 17 сентября 2010

Возможно, вы захотите переосмыслить это.

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()

Вызываемые объекты могут упростить вашу жизнь.

1 голос
/ 17 сентября 2010

На самом деле вам нужна функция, которая ничего не делает, но имеет __name__, которая полезна для журнала.Лямбда-функция делает именно то, что вы хотите, но execfunc.__name__ дает "<lambda>".Попробуйте один из них:

def nothing_func():
    return
def ThisAppearsInTheLog():
    return

Вы также можете назначить свои собственные атрибуты для функций:

def log_nothing():
       return
log_nothing.log_info = "nothing interesting"

Затем изменить execfunc.__name__ на getattr(execfunc,'log_info', '')

1 голос
/ 17 сентября 2010
>>> import dis
>>> f = lambda: None
>>> dis.dis(f)
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE    

>>> g = lambda: Pass 
>>> 
>>> 
>>> dis.dis(g)
  1           0 LOAD_GLOBAL              0 (Pass)
              3 RETURN_VALUE 


>>> g = lambda: pass 
  File "<stdin>", line 1
    g = lambda: pass 
                   ^
SyntaxError: invalid syntax
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...