【问题标题】:Looking for idiomatic way to evaluate to False if argument is False in Python 3如果 Python 3 中的参数为 False,则寻找惯用的方法来评估为 False
【发布时间】:2019-03-22 11:32:13
【问题描述】:

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

fus(roh(dah(inp)))

其中inp 是字典或bool(False)

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

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

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

如果 i == False 抛出 TypeError,bool not subscriptable,因为 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'] 引发的 KeyError,其中 dict 为空。

【问题讨论】:

  • 你控制inp吗?你知道空字典是假的吗?
  • 你能返回一个空字典而不是False吗?至少到那时,您可以专注于编写始终适用于字典的函数。
  • @chepner 上帝,我正要评论“寻找 Maybe monad 不是吗?” :)
  • @Jefe 你确定inp 在引发异常时是False 吗?尝试在函数的第一行打印inp
  • return int(inp['value']) + 1 if inp else False 应该可以工作,因为 x if y else z 仅在选择了表达式的那一侧时才计算 xz。你还有其他问题。

标签: python python-3.x


【解决方案1】:

装饰器应如下所示:

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

【讨论】:

  • 在课堂上实现这个有什么建议吗?如果在类中定义为 validate_inp(self, fun) 我会得到一个缺少参数类型的错误,并且将函数显式传递给装饰器调用是没有意义的,也不起作用。另一方面,如果我在类外部将其定义为 validate_inp(fun) 我会得到一个“接受 1 个参数,给定 2 个”类型错误。
  • 我也尝试在类中定义为 validate_inp(fun) 并将包装器定义为 wrapper(self, inp),但是我只是得到相同的缺少参数类型错误,这次是从包装的功能。
  • @Jefe 我添加了一个如何将装饰器与类成员一起使用的示例。
  • 感谢您的帮助。在这样的类中实现时,装饰器似乎会中断。包装的函数正确评估一个有效的字典,但返回一个 TypeError,布尔值在传递 False 时不可下标。装饰器似乎对功能没有影响。
  • 上面的代码是否返回TypeError
【解决方案2】:

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

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

如果 i == False 会抛出 TypeError,bool not subscriptable,因为 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

【讨论】:

  • 感谢您的意见。一段时间以来,我一直在使用三元组来取得很好的效果,但看起来我需要回过头来看看我错过的一些灵活性。感谢您提出 functool.wraps。完全忘记了这一点,就像我每次写装饰器一样。
【解决方案3】:

除非您将值直接传递给装饰器,否则不应参数化它。在您的情况下,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 函数返回。从现在开始,每当你调用funcwrapper 将被调用,inp 将只传递给wrapper 函数。然后wrapper会根据inp的值来决定是否调用实际的func


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

>>> 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 作为第一个参数传递。

【讨论】:

    【解决方案4】:

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

    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
      

    把它们放在一起(inp和上面是同一个字典)

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

    【讨论】:

      【解决方案5】:

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

      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 子句。

      【讨论】:

        【解决方案6】:

        只是另一种选择:一个辅助函数,它接受一个起始值和函数并应用函数,只要它不遇到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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-03-15
          • 2014-01-19
          • 2021-07-04
          • 2012-09-11
          • 2019-04-24
          • 2015-03-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多