Josh Katz
Josh Katz

Reputation: 11

how to make a decorator function to be part of class

I want the functions to be part of the class I am building, but I get an error There is probably a problem that the decorator function will be a function in the department. Is there a solution to the problem ? Thank you.

import engineio

class Websocket:

    def __init__(self):
        self.eio = engineio.Client()
        self.eio.connect('http://localhost:5000')
        self.eio.wait()

    # get error in this function       
    @eio.on('connect')
    def on_connect():
        print('connection established')

Upvotes: 1

Views: 170

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121436

You can't use a decorator on a method where the decorator expression refers to an instance attribute. That's because decorators are executed when the function they decorate is being created. Inside a class statement body that means that when decorators are applied, there is no class yet, and without a class, there can't be any instances either.

You have two options:

  • Just call self.eio.on('connect') in the __init__ of your class, passing in a bound method:

    class Websocket:
    
        def __init__(self):
            self.eio = engineio.Client()
            self.eio.connect('http://localhost:5000')
            self.eio.on('connect', self.on_connect)
            # don't call wait, see below
    
        def on_connect(self):
            print('connection established')
    

    This works because by the time __init__ is being called you have a class and an instance of that class (referenced by self), and self.on_connect returns a reference to a bound method (calling it will have self passed in). @.... decorator syntax is just syntactic sugar, you don't have to use it. The engineio.Client.on() method accepts the handler as a second argument, but you could also use self.eio.on('connect')(self.on_connect), which is a literal translation of what the decorator syntax does.

  • Use a nested function inside __init__, and decorate that:

    class Websocket:
    
        def __init__(self):
            self.eio = engineio.Client()
            self.eio.connect('http://localhost:5000')
    
            @self.eio.on('connect')
            def on_connect():
                print('connection established')
    
            # don't call wait, see below
    
    
    but that makes it much harder to use that function directly from other code. 
    

Note that the engineio.Client.wait() method blocks, it doesn't return, until the connection has ended. You would not normally put that kind of call in the __init__ method of a class!

Using a class to handle engineio events is great, but don't start the client connection from the class. I'd do this instead:

class Websocket:
    def __init__(self, socket_url):
        self.eio = engineio.Client()
        self.eio.connect(socket_url)
        self.eio.on('connect', self.on_connect)
        # other registrations

    def on_connect(self):
        # handle connection

    # other handlers

    def wait(self):
        self.eio.wait()

websocket = Websocket('http://localhost:5000)
websocket.wait()

Upvotes: 3

Related Questions