【问题标题】:Python - DecoratorsPython - 装饰器
【发布时间】:2014-01-23 14:02:54
【问题描述】:

我正在努力学习 Decorators 。 我理解了它的概念,现在正在尝试实现它。

这是我写的代码 代码是不言自明的。它只是检查参数是否传入int

def wrapper(func):
    def inner():
        if issubclass(x,int): pass
        else: return 'invalid values'

    return inner()

@wrapper
def add(x,y):
    return x+y

print add('a',2)

global name 'x' is not defined 会抛出错误。我知道它没有在inner 下定义,但不知道如何纠正此代码?我哪里错了?

【问题讨论】:

    标签: python python-decorators


    【解决方案1】:

    你的装饰器应该是这样的:

    def wrapper(func):
        def inner(x, y): # inner function needs parameters
            if issubclass(type(x), int): # maybe you looked for isinstance?
                return func(x, y) # call the wrapped function
            else: 
                return 'invalid values'
        return inner # return the inner function (don't call it)
    

    需要注意的几点:

    • issubclass 需要一个类作为第一个参数(你可以用一个简单的 try/except TypeError 替换它)。
    • 包装器应该返回一个函数,而不是被调用函数的结果
    • 您实际上应该在内部函数中调用包装函数
    • 您的内部函数没有参数

    你可以找到关于装饰器的很好的解释here

    【讨论】:

    • 如果不使用isinstance(),我会使用type(x) 而不是x.__class__
    • 感谢您更正代码。我怀疑包装函数的概念。是否真的需要编写一个嵌套函数来检查值是否为 int。我的意思是如果不使用嵌套函数就无法完成。例如:-def wrapper(x,y): if issubclass(x.__class__, int): return func(x, y) else: return 'invalid values'
    • @MartijnPieters 感谢您的评论;使用type(x) 优于x.__class__。修好了。
    • @python-coder 不,那行不通。当你使用wrapper 作为装饰器时,它会被包装的函数作为第一个参数调用。
    • 为了更加向上兼容,您可以使用灵活的参数列表:def inner(x, *args, **kwargs):return func(x, *args, **kwargs)
    【解决方案2】:

    我发现您当前的代码存在三个问题。

    首先,您调用的是inner 函数,而不是返回对它的引用。

    其次,您的 inner 函数不采用与您正在装饰的函数相同的参数。在这种情况下,您至少需要显式使用 x 参数(某些内部函数可以独占使用 *args**kwargs,但显然不是您的)。

    最后,你永远不会调用被包装的函数。虽然这不是严格要求的(在开发过程中用装饰器替换方法可能很有用),但通常您希望在内部函数代码的某个时间点调用该函数。

    所以,为了把整个事情包装在一起,我认为你希望你的代码是这样的:

    def wrapper(func):
        def inner(x, y):
            if issubclass(x, int): # issue 2
                return func(x, y) # issue 3
            else:
                return "invalid values" # consider raising an exception here instead!
    
        return inner # issue 1
    

    【讨论】:

    • 感谢您更正代码。我怀疑包装函数的概念。是否真的需要编写一个嵌套函数来检查值是否为 int。我的意思是如果不使用嵌套函数就无法完成。例如:-def wrapper(x,y): if issubclass(x.__class__, int): return func(x, y) else: return 'invalid values'
    • 嵌套函数是使其作为装饰器工作所必需的。如果您希望 yoru 代码的用户直接调用 wrapper,而不是通过其通常的名称调用 func(尽管您需要直接在 wrapper 中命名),那么您的替代版本可以工作。请注意,您对函数的命名有点不寻常。通常,外部函数以装饰器的用途命名(例如arg_type_check 或其他东西),wrapper 是装饰器返回的内部函数的名称,“包裹”在原始函数周围。
    【解决方案3】:

    这个怎么样。

    def wrapper(func):
        def inner():
            if isinstance(func,int):
                    return func(x, y)
            else: return 'invalid values'
    
        return inner()
    

    【讨论】:

    • 这很糟糕。 wrapper 应该返回一个函数才能用作装饰器。您的解决方案返回调用inner结果,即。 e.您将返回调用 func 或字符串 ('invalue values') 的结果。也不是函数。
    【解决方案4】:

    这可能有效。

    def wrapper(func):
        def inner(*args,**kwargs):
            if ((args[0] is int) and (args[1] is int)): pass
            else: return 'invalid values'
        return inner
    @wrapper
    def add(x,y):
        return x+y
    print add('a',2)
    

    【讨论】:

      【解决方案5】:

      如果您想在类型检查失败时正确终止 add 方法,您也可以引发异常。像这样

      def check_int_types(func):
          def type_checker(x, y):
              if issubclass(type(x), int) and issubclass(type(y), int):
                  return func(x, y)
              raise Exception("Invalid types: {}, {}".format(x, y))
          return type_checker
      
      @check_int_types
      def add(a, b):
          return a + b
      
      def main():
          x = y = 15.0
          print add(x, y)
      
      if __name__ == '__main__':
          main()
      

      结果:

      Traceback (most recent call last):
        File "deco_test.py", line 17, in <module>
          main()
        File "deco_test.py", line 14, in main
          print add(x, y)
        File "deco_test.py", line 5, in type_checker
          raise Exception("Invalid types: {}, {}".format(x, y))
      Exception: Invalid types: 15.0, 15.0
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-07
        • 1970-01-01
        • 2021-05-20
        • 2020-02-10
        • 2011-11-04
        • 2011-04-28
        • 1970-01-01
        相关资源
        最近更新 更多