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

Looking for idiomatic way to evaluate to False if argument is False in Python 3

我有一个函数链,所有函数都在类的其他地方定义:

1
fus(roh(dah(inp)))

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

期望的结果是,如果inp或任何函数对False进行计算,则函数堆栈将返回False

我试图使用三元运算符,但它们的计算不正确。

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

如果i == False是因为inp['value']是在条件之前计算的,则抛出typeerror,bool不可订阅。

我知道我可以明确地做到:

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

但是有很多函数,这将使我的代码长度增加近四倍。它还一次又一次地重写完全相同的代码行,这对我来说意味着这是错误的做法。

我怀疑一个有争论的装饰师是答案,但我越是玩弄它,我就越不确定自己是怎么想的。

1
2
3
4
5
6
7
8
9
10
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

不幸的是,decorator调用引发了一个名称错误,未定义"inp"。但我不确定我是否使用了错误的装饰,或者装饰是错误的解决方案。

寻求评论、批评、建议和/或理智检查。

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

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

在我的应用程序中,使用false是"可以的",但没有提供任何优势,并且导致了一些代码块的厚重。

我发现用一本空字典来代替一切都比较简单。我将使用dict的函数与一个修饰符包装起来,该修饰符捕获通过引用dict['value'],其中dict为空而引发的键错误。


装饰师应该看起来像:

1
2
3
4
5
6
7
8
9
10
11
12
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}))

如果要将decorator与类成员一起使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
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}))


I attempted to use ternary operators, but they don't evaluate correctly.

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

throws a TypeError, bool not subscriptable, if i == False because inp['value'] is evaluated before the conditional.

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

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

要自动包装这样的函数,请生成包装函数的函数:

1
2
3
4
def fallthrough_on_false(function):
    def inner(inp):
        return inp and function(inp)
    return inner

这应该通过使用functools.wraps进行修饰和名称来加以改进,并且它可能需要可变数量的参数来允许可选扩展:

1
2
3
4
5
6
7
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


除非您将一个值直接传递给decorator,否则不应将其参数化。在您的例子中,inp实际上是传递给函数的,而不是传递给修饰器的。因此,实现变成这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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

这两条线

1
2
@validate_inp
def func(inp):

可以这样理解

1
func = validate_inp(func)

因此,func实际上是wrapper函数,由validate_inp函数返回。从现在起,每当您调用func时,都会调用wrapper,并且只将inp传递给wrapper函数。然后由wrapper根据inp的值决定是否调用实际的func

如果要在一个类中实现同一个修饰器,只需考虑wrapper函数中的第一个参数self。就是这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> 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作为第一个参数传递即可。


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

1。用dictionary.get代替[]。在这里,您可以定义一个回退值。例如。

1
2
3
4
5
In [1548]: inp
Out[1548]: {'6': 'Hi'}

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

    1
    2
    In [1550]: isinstance(inp, dict)
    Out[1550]: True

  • 把它们放在一起(inp和上面的字典一样)

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    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)

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

    1
    2
    3
    4
    5
    6
    7
    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。然后,只需在调用堆栈的顶部捕获一次异常,但首先包装输入。

    1
    2
    3
    4
    try:
        x = fus(roh(dah(assert_not_false(inp))))
    except FalseInput:
       x = False

    这也可能更有效,因为您不必调用所有函数;如果inpFalse开始,assert_not_false将立即引发异常,您将直接跳到except子句。