如果Python 3中的参数为False,则寻找评估为False的惯用方法

时间:2015-05-07 17:08:03

标签: python python-3.x

我有一系列函数,都在类的其他地方定义:

fus(roh(dah(inp)))

其中inp是字典,或bool(False)

期望的结果是,如果inp或任何函数求值为False,则函数堆栈将返回False

我尝试使用三元运算符,但它们没有正确评估。

def func(inp):
    return int(inp['value']) + 1 if inp else False

抛出一个TypeError,bool不可订阅,如果i == False因为inp['value']在条件之前被评估。

我知道我可以明确地做到这一点:

def func(inp):
    if inp == False:
        return False
    else:
        return inp['value'] + 1

但是有很多功能,这几乎会使我的代码长度增加四倍。它也会一次又一次地重写完全相同的代码行,这对我来说这是错误的做事方式。

我怀疑有一个带有参数的装饰者就是答案,但是我玩的越多,我就越不确定。

def validate_inp(inp):
    def decorator(func):
        def wrapper(*args):
             return func(inp) if inp else False
        return wrapper
    return decorator

@validate_inp(inp)
def func(inp):
    return int(inp['value']) + 1

不幸的是,装饰器调用抛出一个NameError,'inp'未定义。但我不确定我是否错误地使用了装饰器,或装饰器是错误的解决方案。

寻找评论,批评,建议和/或理智检查。

如果你发现这个试图解决你自己的问题......

您可能希望使用空字典而不是布尔值False。道具到@chepner。

在我的应用程序中,使用False是“没问题”,但没有任何优势,并且导致了一些庞大的代码块。

我发现使用空字典更简单了。我正在使用装饰器包装使用dict的函数,该装饰器通过引用dict['value']引用的dict来捕获引发的KeyError,其中records.group_by { |h| h[:id] }.map { |id, arr| { id: id, note: arr.map { |h| h[:note] }.join } } #=> [{:id=>"PU525", :note=>"FooBar"}, # {:id=>"DT525", :note=>"Hello World"}, # {:id=>"PU680", :note=>"Fubar"}] 为空。

6 个答案:

答案 0 :(得分:9)

装饰者应该看起来像:

def validate_inp(fun):
    def wrapper(inp):
        return fun(inp) if inp else False
    return wrapper


@validate_inp
def func(inp):
    return int(inp['value']) + 1

print(func(False))
print(func({'value': 1}))

如果您想将装饰者与班级成员一起使用:

def validate_inp(fun):
    def wrapper(self, inp):
        return fun(self, inp) if inp else False
    return wrapper

class Foo(object):
    @validate_inp
    def func(self, inp):
        return int(inp['value']) + 1 if inp else False

foo = Foo()
print(foo.func(False))
print(foo.func({'value': 1}))

答案 1 :(得分:8)

  

我尝试使用三元运算符,但它们没有正确评估。

def func(inp):
    return int(inp['value']) + 1 if inp else False
     

抛出一个TypeError,bool不可订阅,如果i == False因为inp['value']在条件之前被评估。

这不是真的 - 代码有效。此外,你可以写

def func(inp):
    return inp and (int(inp['value']) + 1)

要自动包装这样的函数,请创建一个包装函数的函数:

def fallthrough_on_false(function):
    def inner(inp):
        return inp and function(inp)
    return inner

这应该通过使用functools.wraps来传递装饰器和名称来改进,并且它应该采用可变数量的参数来允许可选扩展:

from functools import wraps

def fallthrough_on_false(function):
    @wraps(function)
    def inner(inp, *args, **kwargs):
        return inp and function(inp, *args, **kwargs)
    return inner

答案 2 :(得分:4)

除非您将值直接传递给装饰器,否则不应对其进行参数化。在您的情况下,inp实际上是传递给函数,而不是传递给装饰器。所以,实现就像这样

>>> def validate_inp(f):
...     def wrapper(inp):
...          if not inp:
...              return False
...          return f(inp)
...     return wrapper
... 
>>> @validate_inp
... def func(inp):
...     return int(inp['value']) + 1
... 
>>> func(False)
False
>>> func({'value': 1})
2

这两行

@validate_inp
def func(inp):

可以像这样理解

func = validate_inp(func)

因此,func实际上是由wrapper函数返回的validate_inp函数。从现在开始,无论何时拨打func,都会调用wrapperinp只会传递给wrapper。然后wrapper将决定是否根据func的值调用实际inp

如果要在类中实现相同的装饰器,则只需考虑self函数中的第一个参数wrapper。多数民众赞成。

>>> class Test(object):
... 
...     def validate_inp(fun):
...         def wrapper(self, inp):
...             if not inp:
...                 return False
...             return fun(self, inp)
...         return wrapper
...     
...     @validate_inp
...     def func(self, inp):
...         return int(inp['value']) + 1
...     
... 
>>> Test().func(False)
False
>>> Test().func({'value': 1})
2

由于wrapper是实际func,因此它也会接受selfinp。当您调用函数f(实际为func)时,您只需将self作为第一个参数传递。

答案 3 :(得分:3)

这可能不是你想要的,但这些有助于你的事业吗?

1。 使用dictionary.get而不是[]。您可以在此处定义回退值。例如。

In [1548]: inp
Out[1548]: {'6': 'Hi'}

In [1549]: inp.get('5',99)
Out[1549]: 99
  1. isinstance可用于检查变量是否为字典。

    In [1550]: isinstance(inp, dict)
    Out[1550]: True
    
  2. 将它们放在一起(inp与上面的字典相同)

    In [1554]: print "True" if isinstance(inp, dict) and len(inp.keys()) else "False"
    True
    

答案 4 :(得分:2)

一个选项可能是定义自定义异常和一个小包装器:

class FalseInput(Exception): pass
def assert_not_false(inp):
    # I'll assume `inp` has to be precisely False,
    # and not something falsy like an empty dictionary.
    if inp is False:
        raise FalseInput
    return inp

修改每个函数以引发相同的异常,而不是返回False。然后,只需在调用堆栈的顶部捕获异常一次,但首先包装输入。

try:
    x = fus(roh(dah(assert_not_false(inp))))
except FalseInput:
   x = False

这也可能更有效,因为您不一定需要调用所有函数;如果inpFalse开头,assert_not_false会立即引发异常,您将直接跳转到except条款。

答案 5 :(得分:2)

另一种选择:一个辅助函数,它接受起始值和函数并应用函数,只要它不会遇到False

def apply(x, *functions):
    for func in functions:
        if x is False:
            return x      # or break
        x = func(x)
    return x

outp = apply(inp, dah, roh, fus)