【问题标题】:Decorators around function with *args带有 *args 的函数周围的装饰器
【发布时间】:2016-01-28 08:30:45
【问题描述】:

我尝试学习 python 装饰器,因为这些东西非常有用。我了解简单的装饰器是如何工作的,但我试图围绕需要 *args 的函数制作装饰器,但它不起作用。当然,我错过了一些元素。我一直在浏览互联网寻找答案,但我找不到答案。

内部函数将列表中的所有整数相加,包装器应该检查列表中的所有元素是否都是整数。

def wrapper(func):
    def inner(*args):
        for i in range(0, len(args[0])): #It does not iven get there.
            if not isinstance(i, int):
                return 'Invaild values.'
        else:       
            return func(*args)
    return inner

def add(*args):
    result = 0
    for i in range(0, len(args[0])):
        result += args[0][i]
    return result


def main():
    numbers = [1, '2', 3, 4]
    print(add(numbers))
    numbers = [1, 2, 3, 4]
    print(add(numbers))


if __name__ == '__main__':
    main()

我得到的错误。

Traceback (most recent call last):
  File "wrapper_of_function.py", line 23, in <module>
    main()
  File "wrapper_of_function.py", line 20, in main
    print(add(numbers))
  File "wrapper_of_function.py", line 15, in add
    result += args[0][i]
TypeError: unsupported operand type(s) for +=: 'int' and 'str'

【问题讨论】:

  • 你在哪里使用wrapper
  • @Zetys,我知道,包装的目的是检查ints中是否有str,如果有,应该返回'Invaild values'

标签: python function decorator


【解决方案1】:

你测试i 是否是int 的实例,什么时候你应该测试args[0][i] 是否是int 的实例:

def wrapper(func):
    def inner(*args):
        for i in range(len(args[0])): #It does not iven get there.
            if not isinstance(args[0][i], int):
                return 'Invalid values.'       
        return func(*args)
    return inner

@wrapper
def add(*args):
    result = 0
    for i in range(0, len(args[0])):
        result += args[0][i]
    return result


def main():
    numbers = [1, '2', 3, 4]
    print(add(numbers))
    numbers = [1, 2, 3, 4]
    print(add(numbers))


if __name__ == '__main__':
    main()

【讨论】:

    【解决方案2】:

    也许你打算实际使用你的装饰器

    def wrapper(func):
        def inner(*args):
            for i in args[0]:
                if not isinstance(i, int):
                    raise Exception('a non integer was encountered')
            else:       
                return func(*args)
        return inner
    
    @wrapper
    def add(*args):
        total = 0
        for i in args[0]:
            total += i
        return total
    
    
    def main():
        numbers = [1, '2', 3, 4]
        try:
            print(add(numbers))
        except Exception as e:
            print 'invalid input %r - %s' % (numbers, e)
        numbers = [1, 2, 3, 4]
        print(add(numbers))
    
    
    if __name__ == '__main__':
        main()
    

    请注意,此实现的一个弱点是wrapped 必须了解传递给它的内容才能正常工作。它需要知道检查第 0 个元素。对于其他目的,这不会是一个非常有用的功能。即使您使用*args,您仍然只将一个参数传递给add。如果您确实将多个参数传递给它所装饰的任何东西,您可能会发现 wrapped 通常更有用。

    这样做

    def wrapper(func):
        def inner(*args):
            for i in args:
                if not isinstance(i, int):
                    raise Exception('a non integer was encountered')
            else:       
                return func(*args)
        return inner
    
    @wrapper
    def add(*args):
        total = 0
        for i in args:
            total += i
        return total
    
    
    def main():
        numbers = [1, '2', 3, 4]
        try:
            print(add(*numbers))
        except Exception as e:
            print 'invalid input %r - %s' % (numbers, e)
        numbers = [1, 2, 3, 4]
        print(add(*numbers))
    
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 这是可靠的。我们做了几乎相同的事情:)
    【解决方案3】:

    抛出的类型错误是因为您无法像 [1,'2',3,4] 中那样添加 Int 和 Str。您的装饰者应该在系统抛出错误之前执行检查,但您并没有真正使用它。

    此外,还有一些错误。

    def wrapper(func):
        def inner(*args):
            # 1, you don't use args other than args[0], what's the point of using list arguments?
            for i in range(0, len(args[0])): #It does not iven get there.
                # 2, you are running isinstance on array indexes, you should run this on values.
                if not isinstance(i, int):
                    # 3, it's a bad practice to return different types in different scenarios. Return a special value and print the error message, or just raise a exception
                    return 'Invaild values.'
            # 4, else keyword is redundant. the if statement is inside the loop.
            else:       
                return func(*args)
        return inner
    
    # 5, you didn't use wrapper at the first place.
    def add(*args):
        result = 0
        # 6, only using the first argument rather than the list
        for i in range(0, len(args[0])):
            result += args[0][i]
        return result
    

    我为你重写了这段代码,希望它能按你的需要工作。

    def wrapper(func):
        def inner(*args):
            for arg in args:
                if not isinstance(arg, int):
                    raise TypeError('Invalid values')
            return func(*args)
        return inner
    
    @wrapper
    def add(*args):
        result = 0
        for arg in args:
            result += arg
        return result
    

    检查是否有效

    【讨论】:

    • 感谢您指出一些事情,@4 else 可以这样使用。 @5 我没有,明显的错误。您的回答无效。
    • @frankot else 可以这样使用而不会出现语法错误,但这对我来说有点不自然。我猜是个人喜好。它对我有用。你对这个功能有什么期望?
    • 如果列表中只有整数则不起作用。即使不应该引发错误。关键是用消息提示用户而不是引发错误,因为错误会停止程序。
    • @frankot 我明白了。这个 add 函数需要几个参数而不是一个列表。这就是列表参数的目的。例如,如果您调用 add(1,2,3,4),它将起作用。如果你调用 add([1,2,3,4]),参数列表 *args 实际上是一个列表,只包含一个列表参数,并且会引发错误。这就是区别,如果你要使用任意参数,你应该使用我的函数。如果您想要一个列表,只需使用 add(alist),而不是 add(*args)。关于异常,我强烈建议你使用 try 和 catch 来继续程序而不是产生一个字符串。
    • 我现在明白我误解了*args 的目的和作用,感谢您为我澄清这一点。我知道trycatch 更灵活,但对于这种简单的用法来说,它看起来很重要。此外,您的评论向我解释了为什么数字列表在一个 touple 内。
    【解决方案4】:

    你应该在add函数上使用@wrapper

    def wrapper(func):
        def inner(args):
            for item in args:
                if not isinstance(item, int):
                   return  'Invalid value found'
            return func(args)
        return inner
    
    @wrapper    
    def add(args):
        return sum(args)
    
    
    def main():
        numbers = [1, '2', 3, 4]
        print(add(numbers))
        numbers = [1, 2, 3, 4]
        print(add(numbers))
    
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 但是当字符串在列表中传递时,这个答案并没有超过“无效值”。然而,代码是紧凑和 Pythonic 的。
    • @frankot 我添加了'Invalid values'
    • @Zetys 所以你要返回一个异常?
    • @Zetys 好的,谢谢。它有效,但你能解释一下'TypeError'在这种情况下的作用,除了打印'invaild values'。我不喜欢使用我不懂的东西。
    • @frankot TypeError 是当操作或函数应用于不适当类型的对象时引发的异常。关联的值是一个字符串,提供有关类型不匹配的详细信息。
    【解决方案5】:

    @Zetys : 在 numbers = [1, '2', 3, 4] 中,第二个元素是一个字符串 所以它不能添加到result,因为它是int(结果= 0)。如果您希望 string 参数转换为 int 以防它们是数字,您应该使用 int(str) 函数。所以你的代码将是:

    def add(*args):
        result = 0
        for i in range(0, len(args[0])):
            result += int(args[0][i])
        return result
    

    顺便说一句,你没有在代码的任何地方使用wrapper 类,所以它不会运行:)

    【讨论】:

      猜你喜欢
      • 2014-07-21
      • 2022-09-23
      • 2013-02-10
      • 1970-01-01
      • 2018-11-21
      • 1970-01-01
      • 2023-03-17
      • 2020-07-15
      • 1970-01-01
      相关资源
      最近更新 更多