【问题标题】:'NoneType' object is not callable with decorator class'NoneType' 对象不能用装饰器类调用
【发布时间】:2019-03-17 22:23:20
【问题描述】:

我正在学习使用此资源的装饰器类:
http://book.pythontips.com/en/latest/decorators.html#decorator-classes

呈现的类基本上是这样的:

class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile

    def __call__(self, func):
        log_string = func.__name__ + " was called"
        print(log_string)
        # Open the logfile and append
        with open(self.logfile, 'a') as opened_file:
            # Now we log to the specified logfile
            opened_file.write(log_string + '\n')
        # Now, send a notification
        self.notify()

    def notify(self):
        # logit only logs, no more
        pass

和电话:

@logit()
def myfunc1():
    pass

myfunc1()

我收到一个错误:

>>> myfunc1()
[...]
TypeError: 'NoneType' object is not callable

【问题讨论】:

  • 鉴于您的调用,确保您的 __call__ 方法返回一个可调用的(可能是 return func?)

标签: python class decorator python-decorators


【解决方案1】:

logit.__call__ 确实返回None,而您正在通过装饰做myfunc1 = logit()(myfunc1)myfunc 现在是 None

据我了解,您希望记录装饰函数的每次调用。在这种情况下,__call__ 必须构建一个新函数并 return 它。

类似

def __call__(self, func):
    def new_func(*args, **kwargs):
        log_string = func.__name__ + " was called"
        print(log_string)
        # Open the logfile and append
        with open(self.logfile, 'a') as opened_file:
            # Now we log to the specified logfile
            opened_file.write(log_string + '\n')
        # Now, send a notification
        self.notify()

        # execute func
        return func(*args, **kwargs)
    return new_func

现在

@logit()
def myfunc1():
    pass

myfunc1 = logit()(myfunc1)

即它将名称myfunc1 重新分配给__call__ 中内置的新函数。这个新函数执行日志记录逻辑,然后执行旧的myfunc1,它仍然保留在名称func 下作为闭包变量。

【讨论】:

  • 感谢您提及 new_func 以及为什么需要它(或不需要它,具体取决于用例)。对于任何有兴趣的人,我尝试在有和没有它的情况下致电my_func()。没有new_func,第二次调用my_func() 没有调用__call__。有了它,每次调用my_func(),都会调用__call__
  • @user3132457 不完全是。在这两种情况下,__call__ 只被调用一次。但是在第二个示例中,您使用在第一个示例中生成的新函数覆盖myfunc1,并且只调用__call__。这个新函数恰好执行日志记录逻辑,因此现在每次调用myfunc1 时都会发生日志记录。
  • 好的,但是为什么在第二次调用myfunc1 时没有new_func 不会发生日志记录? 编辑:顺便说一句,我在__call__ 中设置了一个断点,而没有new_func 并且断点只被击中一次。怎么来的?
  • @user3132457 这两个问题的答案是__call__ 只被调用了一次。当你启动脚本时,myfunc1 = logit().__call__(myfunc1) 被执行。此后不再致电__call__
  • @user3132457 我最近解释了装饰器是如何工作的here,也许你会发现它很有用。关键是装饰器只被调用一次来生成一个新函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 2020-08-19
  • 2013-01-10
  • 2021-11-22
  • 1970-01-01
  • 2020-09-22
  • 2019-11-18
相关资源
最近更新 更多