自定义装饰器不允许在主Python中调用函数

时间:2015-07-01 14:04:24

标签: python decorator python-decorators

在我构建的特定类中,它基本上充当用户定义函数的包装器,除了实例化之外,用户只会使用我的类中的2-3个主要函数。我试图使我的类装饰器兼容,以便用户可以简单地构建他的函数并使用装饰器而不是从我构建的类中显式调用2-3个函数。我现在正试图测试这个。

主:

from class_I_built import ClassIBuilt

def main():
    testfcn()


@ClassIBuilt('1', MessageConverter, timeout=10)
def testfcn():
    count = 0
    while count < 1000:
        count = count + 1

main()

class_I_built

import socket
from apb import APB
import config
import time

MCAST_GRP = config.MCAST_GRP
MCAST_PORT = config.MCAST_PORT


class ClassIBuilt(APB):
    processvar = 0

    def __init__(self, id, message_protocol, timeout=config.TIMEOUT):

        self.sock = 1

        self.p_id = p_id
        self.timeout = timeout
        self.message_protocol = message_protocol

    def __call__(self, decorator_function=1):
        self.publish_start('starting message')
        decorator_function(*args, **kwargs)
        self.publish_end('ending message')

    def connect(self):
        ttl = 2
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)

    def publish(self, message_type='alert', message=''):


        self.connect()
        s = self.message_protocol(message)
        try:
            self.sock.sendto(s, (MCAST_GRP, MCAST_PORT))
        finally:
            self.sock.close()

    def publish_start(self, message=''):
        print 'publishing start'
        self.publish("start", message)

    def publish_end(self, message=''):
        print 'publishing end'
        self.publish('end', message)

如果我不在main()中调用testfcn(),则调用__call__方法,并正常发布开始和结束消息。它应该不会这样工作,因为它应该只运行testfcn()如果我在main()中显式调用testfcn()。但是,如果我在main()中调用testfcn(),如上所示,那么它将发送消息但我得到一个NoneType对象是不可调用的错误。

输出/堆栈跟踪:

publishing start
publishing end
Traceback (most recent call last):
  File "Main.py", line 13, in <module>
    main()
  File "Main.py", line 4, in main
    testfcn()
TypeError: 'NoneType' object is not callable

为什么会出现错误,以及如何构建我的代码,以便只有在main中显式调用该函数时它才会运行?

1 个答案:

答案 0 :(得分:3)

您的装饰线创建了一个类的实例:

@ClassIBuilt('1', MessageConverter, timeout=10)

然后该实例用于进行实际装饰;你实际上是这样做的:

decorator = ClassIBuilt('1', MessageConverter, timeout=10)
def testfcn(): # ...
testfcn = decorator(testfcn)

由于decorator是您班级的一个实例,decorator(testfcn)调用由ClassIBuilt.__call__()方法处理,而该方法不返回任何内容

def __call__(self, decorator_function=1):
    self.publish_start('starting message')
    decorator_function(*args, **kwargs)
    self.publish_end('ending message')

没有return语句表示返回None,因此testfcn设置为None。您无法致电None,导致您看到异常。

您的__call__应该返回一个包装器:

def __call__(self, decorator_function=1):
    def wrapper(*args, **kwargs):
        self.publish_start('starting message')
        decorator_function(*args, **kwargs)
        self.publish_end('ending message')
    return wrapper

现在testfcn绑定到wrapper()函数,然后调用原始修饰函数。