dami27 发表于 2022-11-9 16:03:04

装饰器问题

想问一下为什么下面第一种情况是OK的,第二种就会报错呢

第一种
class Logging:
    def __init__(self, func):
      self.func = func
    def __call__(self):
      print(f"{self.func.__name__}()")
      self.func()

      
@Logging
def say():
    print("hello")

say()

say()
hello

第二种
class Logging:
    def __init__(self, func, level="INFO"):
      self.func = func
      self.level = level
    def __call__(self):
      print(f"[{self.level}: {self.func}()]")
      self.func()

      
@Logging(level="ERROR")
def say():
    print("hello")

say()


Traceback (most recent call last):
File "C:\Users\JiJing\Desktop\test.py", line 10, in <module>
    @Logging(level="ERROR")
TypeError: Logging.__init__() missing 1 required positional argument: 'func'

tommyyu 发表于 2022-11-9 16:14:26

本帖最后由 tommyyu 于 2022-11-9 18:42 编辑

原来的代码相当于 say = Logging(level = "ERROR")(say),此时因为 level 参数和 func 参数分批传入,python 就会报错。如果想要实现这种效果,可以这样写class Logging:
    def __init__(self, func, level="INFO"):
      self.func = func
      self.level = level
    def __call__(self):
      print(f"[{self.level}: {self.func}()]")
      self.func()

def temp(level):
    def inner(func):
      nonlocal level
      return Logging(func, level)
    return inner

@temp(level = 'INFO')
def say():
    print('hello')

say()

jackz007 发表于 2022-11-9 18:21:07

class Logging:
    def __init__(self , level = "INFO"):
      self . level = level

    def __call__(self , func_name):
      def wrapper(* args , ** kwargs):
            print(self . level)
            return func_name(* args , ** kwargs)
      return wrapper

@Logging("ERROR")
def say():
    print("hello")

say()

dami27 发表于 2022-11-10 09:44:22

tommyyu 发表于 2022-11-9 16:14
原来的代码相当于 say = Logging(level = "ERROR")(say),此时因为 level 参数和 func 参数分批传入,pytho ...

此时因为 level 参数和 func 参数分批传入,python 就会报错。
:这句话不太理解,什么叫分批传入,是level和func一起传入,python无法分辨的意思吗

tommyyu 发表于 2022-11-10 09:47:21

dami27 发表于 2022-11-10 09:44
此时因为 level 参数和 func 参数分批传入,python 就会报错。
:这句话不太理解,什么叫分批传入,是le ...

在初始化的时候,应该 Logging(func, level) 这样传参,但是因为装饰器的语法原因,你原来的那个 @Logging(level="ERROR")
def say():
    print("hello")这个函数的定义被 Python 解析成了
def say():
    print("hello")
say = Logging(level = 'ERROR')(say)此时 Python 就会报错。

dami27 发表于 2022-11-10 09:47:44

jackz007 发表于 2022-11-9 18:21


可是为啥这样也会报错?装饰器的函数引用不是不用特意传入的吗{:10_266:}

class Logging:
    def __call__(self, func):
      def wrapper():
            print(f"{func.__name__}()")
            return func()
      return func

   
@Logging
def say():
    print("hello")

Traceback (most recent call last):
File "C:\Users\JiJing\Desktop\1.py", line 10, in <module>
    def say():
TypeError: Logging() takes no arguments

jackz007 发表于 2022-11-10 09:50:53

本帖最后由 jackz007 于 2022-11-10 13:17 编辑

dami27 发表于 2022-11-10 09:47
可是为啥这样也会报错?装饰器的函数引用不是不用特意传入的吗
      装饰器类在带和不带参数时,参数的传递约定不同,你只要记住就可以了,不带参数时的被装饰函数是在 __init__() 时传入的。任何时候,装饰器类都不可以少了 __init__() 和 __call__(),一个也不能少!!!
class Logging:
    def __init__(self , func):
      self . func = func
    def __call__(self, * args , ** kwargs):
      print(f"{self . func . __name__}()")
      return func(* args , ** kwargs)

@Logging
def say():
    print("hello")
页: [1]
查看完整版本: 装饰器问题