Использование клиента веб-сокета в качестве класса в python

Я пытаюсь получить доступ к некоторым данным с помощью веб-сокетов, но я не могу обойти примеры, приведенные в документации по веб-сокетам.

У меня есть этот код (https://pypi.org/project/websocket_client/), и я хочу преобразовать его в класс.

import websocket
import thread
import time

def on_message(ws, message):
    print message

def on_error(ws, error):
    print error

def on_close(ws):
    print "### closed ###"

def on_open(ws):
    def run(*args):
        for i in range(3):
            time.sleep(1)
            ws.send("Hello %d" % i)
        time.sleep(1)
        ws.close()
        print "thread terminating..."
    thread.start_new_thread(run, ())


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://echo.websocket.org/",
                                on_message = on_message,
                                on_error = on_error,
                                on_close = on_close)
    ws.on_open = on_open

    ws.run_forever()

Идея состоит в том, чтобы иметь всю эту функциональность веб-сокета в классе, чтобы я мог просто создать объект этого класса.

Я пытался начать это делать, но я не могу пройти даже это:

class MySocket(object):
    def __init__(self):
        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
                                    on_message = on_message,
                                    on_error = on_error,
                                    on_close = on_close)

    def on_message(ws, message):
        print message

    def on_error(ws, error):
        print error

    def on_close(ws):
        print "### closed ###"

    def on_open(ws):
    ws.send("Hello %d" % i)

Ошибка начинается сразу в on_message, говоря, что это «неразрешенная ссылка».


person jbssm    schedule 17.11.2014    source источник


Ответы (8)


Упакуйте вызов в анонимную функцию lambda, чтобы получить правильный вызов с правильным self:

class Client:
    def __init__(self, db, symbols):
        self.ws = websocket.WebSocketApp("wss://the.server.com/api",
                    on_message = lambda ws,msg: self.on_message(ws, msg),
                    on_error   = lambda ws,msg: self.on_error(ws, msg),
                    on_close   = lambda ws:     self.on_close(ws),
                    on_open    = lambda ws:     self.on_open(ws))

    def on_message(self, ws, message):
            msg = json.loads(message)
            print(msg)
    ...
person Eugene    schedule 01.12.2018
comment
Это похоже на правильный способ решения проблемы - person KaZyKa; 02.12.2018
comment
Без лямбд может быть проще: on_message=self.on_message, on_error=self.on_error, ... - person gil9red; 26.06.2020
comment
@gil9red без лямбда-выражений вы потеряете self. Другими словами, внутри on_message, self не будет указывать на экземпляр вашего объекта. - person Eugene; 11.09.2020
comment
Большое спасибо. сэкономьте мне часы. - person chrisckwong821; 03.10.2020
comment
Я пробовал и с lambda, и с self.on_message, но все равно получаю on_open() missing 1 required positional argument: 'ws' - person Jeremiah Payne; 08.10.2020

WebSocketApp нужны вызываемые объекты для своих обратных вызовов (как те, которые вы передаете в конструкторе, например on_message, так и тот, который вы устанавливаете постфактум, on_open).

Обычные функции являются вызываемыми объектами, поэтому ваша не-OO версия работает нормально, потому что вы передаете простые функции.

Связанные методы являются также вызываемыми объектами. Но ваша версия OO не передает связанные методы. Связанный метод, как следует из названия, привязан к объекту. Вы делаете это, используя нотацию obj.method. В вашем случае это self.on_message:

self.ws = websocket.WebSocketApp("ws://echo.websocket.org/",
                                 on_message = self.on_message,
                                 on_error = self.on_error,
                                 on_close = self.on_close)
self.ws.on_open = self.on_open

Однако у вас есть другая проблема. Хотя это устранит вашу ошибку, это не заставит ваш код работать. Обычный метод должен принимать self в качестве первого аргумента:

def on_message(self, ws, message):
    print message

Также стоит отметить, что на самом деле вы используете класс ни для чего. Если вы никогда не обращаетесь к чему-либо вне self, класс просто действует как пространство имен. Не то чтобы это всегда было плохо, но обычно это признак того, что вам нужно хотя бы продумать свой дизайн. Есть ли действительно какое-то состояние, которое вам нужно поддерживать? Если нет, то зачем вообще нужен класс?

Вы можете перечитать учебный раздел по классам, чтобы понять методы, self , и т.д.

person abarnert    schedule 17.11.2014
comment
Ну, я не могу заставить его работать так. Я пытаюсь: ws = MySocket(); ws.run_forever(); и я получаю сообщение об ошибке: объект «MySocket» не имеет атрибута «run_forever». Но, возможно, вы правы, я должен поместить это в класс. - person jbssm; 17.11.2014
comment
@jbssm: Ну, вы определяете класс MySocket, у которого нет метода run_forever(). Вы создаете экземпляр этого класса и пытаетесь вызвать для него run_forever(). Чего вы ожидаете? Если вы хотите, чтобы ваш класс автоматически делегировал все неизвестные методы self.ws, вы можете сделать это, но для этого вам нужно написать код. - person abarnert; 18.11.2014
comment
Спасибо. Я перестану использовать класс и попытаюсь использовать обычные функции. - person jbssm; 18.11.2014

import websocket

try:
    import thread
except ImportError:
    import _thread as thread
import time


class OnyxGenericClient:
    """
    Onyx Client Interface

    """

    def __init__(self, ):
        websocket.enableTrace(True)
        ws = websocket.WebSocketApp("ws://localhost:3000/",
                                         on_message=self.on_message,
                                         on_error=self.on_error,
                                         on_close=self.on_close)
        self.ws = ws
        self.ws.on_open = self.on_open
        self.ws.run_forever()

    # def initiate(self):

    def on_message(self, message):
        print(message)
        return message

    def on_error(self, error):
        return error

    def on_close(self):
        print("### closed ###")

    def run(self, *args):
        global driver
        driver = True
        while driver:
            try:
                time.sleep(1)
                print("Say something nice")
                p = input()
                self.ws.send(p)
            except KeyboardInterrupt:
                driver = False
        time.sleep(1)
        self.ws.close()
        print("thread terminating...")

    def on_open(self):
        thread.start_new_thread(self.run, ())


if __name__ == "__main__":
    websocket.enableTrace(True)
    onyx_client = OnyxGenericClient()

Интересно, почему все до сих пор ставят параметр ws.

Прочтите журнал ошибок.

Файл "venv/lib/python3.7/site-packages/websocket/_app.py", строка 343, в обратном вызове _callback (*args)

    def _callback(self, callback, *args):
    if callback:
        try:
            if inspect.ismethod(callback):
                callback(*args)
            else:
                callback(self, *args)

        except Exception as e:
            _logging.error("error from callback {}: {}".format(callback, e))
            if _logging.isEnabledForDebug():
                _, _, tb = sys.exc_info()
                traceback.print_tb(tb)

Глядя на наши обратные вызовы, on_open(self, ws)

Когда блок try выполняется, он проверяет, является ли наш обратный вызов методом или функцией. если это метод, он будет выполнять callback(*args) уже наше «я» из нашего CustomClient, уже переданного в качестве аргумента в (*args). Имейте в виду, что у него уже есть свой self в def _callback(self, callback, *args). Следовательно, каждый обратный вызов, который является экземпляром вашего CustomClient, не должен иметь аргумент ws.

person Durodola Opemipo    schedule 22.06.2019
comment
Спасибо, чувак @vc74. Даже не помню, когда я ответил на это. Хотя имеет большой смысл. - person Durodola Opemipo; 12.11.2020

Вам нужно добавить «я» к вашим методам класса:

class MySocket(object):
    def __init__(self):
        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
                                on_message = self.on_message,
                                on_error = self.on_error,
                                on_close = self.on_close)

    def on_message(self, ws, message):
        print message

    def on_error(self, ws, error):
        print error

    def on_close(self, ws):
        print "### closed ###"

    def on_open(self, ws):
        ws.send("Hello %d" % i)
person Jon    schedule 17.11.2014
comment
у меня так не работает. Я пытаюсь: ws = MySocket(); ws.run_forever(); и я получаю сообщение об ошибке: объект «MySocket» не имеет атрибута «run_forever». - person jbssm; 17.11.2014
comment
ws не является объектом WebSocketApp. это объект MySocket. Вам нужно будет добавить код в объект, чтобы запустить его, или просто вызвать run_forever для атрибута, например: ws.ws.run_forever() - person Jon; 17.11.2014
comment
Спасибо. Я думаю, что я просто не понимаю принципов, стоящих за этим. Я пока перестану использовать классы и буду использовать обычные функции. - person jbssm; 18.11.2014

The Self делает эти методы как методы класса. Получил, что этот работает как сигнатура методов on_error/message/close, и будет удовлетворен, если будет вызван self, поскольку будет ссылаться на сам класс.

 class MySocket(object):
   def __init__(self,x):
     websocket.enableTrace(True)
     ## Only Keep the object Initialisation here  
     self.x=x
     self.ws=None

     # call This method from a Object and it will create and run the websocket 
    def ws_comm(self):
        self.ws = websocket.WebSocketApp(self.WS_URL,on_message = 
        self.on_message,on_error =self.on_error,on_close = self.on_close)
        self.ws.on_open = self.on_open
        self.ws.run_forever()

    def on_error(self,ws, error):
        print "onError", error

    def on_close(self,ws):
       print "onClosed"

    #Send some message on open 
    def on_open(self,ws):
       self.ws.send(json.dumps(register_msg))

    def on_message(self,ws, msg):
       self.ws.send(json.dumps(msg))


 user1=Userapp('x')
 user1.ws_comm()
person sanchez_30    schedule 14.03.2018

Просто обновление кода, написанного другими авторами на этой странице, помогло мне. Проблема в том, что в определении функций обратного вызова события, таких как on_message, мы не должны использовать ws в качестве параметра. self позаботится об этом, и в теле этих функций обработчика событий мы должны использовать self.ws

class MySocket(object):
    def __init__(self):
        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
                                on_message = self.on_message,
                                on_error = self.on_error,
                                on_close = self.on_close)

    
    def on_message(self, message):
        # if you want to send something use like this
        # self.ws.send()
        print message

    def on_error(self, error):
        print error

    def on_close(self):
        print "### closed ###"

    def on_open(self):
        self.ws.send("Hello Message")
person Tushar Singhal    schedule 13.08.2020

Я хотел бы попробовать так:

class FooClient(object):
    def __init__(self):
        def on_message(ws, message):
            print message
            # use 'self' variable to access other resource
            # handle message from websocket server like this
            self.handler.handle(message)

        def on_error(ws, error):
            print error

        def on_close(ws):
            print "### closed ###"

        def on_open(ws):
            ws.send("Hello %d" % i)

        # assign to 'self.handler'
        self.handler = FooHandler()
        # maybe there are another module should be initiated
        # ...

        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
                                         on_message = on_message,
                                         on_error = on_error,
                                         on_close = on_close)

    def run_forever(self):
        self.ws.run_forever()

    def close(self):
        """clean other resources"""
        pass

Использование внутренней функции в методе __init__(self) могло бы избежать проблемы, связанной с тем, что количество аргументов метода on_message(self, ws, message) не совпадает с количеством аргументов WebSocketApp, предоставляемых его аргументу on_message (метод класса имеет еще один аргумент self).

У меня есть handler выше для обработки сообщения, метод close(self) для очистки некоторых ресурсов, если они у меня есть, run_forever(self) для запуска websocket.

person hhka    schedule 19.10.2018
comment
пожалуйста, опишите решение, которое вы предоставили - person Dinesh Ghule; 19.10.2018
comment
@DineshGhule Я добавил описание, если что-то не так, прокомментируйте. - person hhka; 19.10.2018

Это работает:

class MySocket(object):
    def __init__(self):
        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp("ws://echo.websocket.org:12300/foo",
                                on_message = self.on_message,
                                on_error = self.on_error,
                                on_close = self.on_close)

    @staticmethod
    def on_message(ws, message):
        print message

    @staticmethod
    def on_error(ws, error):
        print error

    @staticmethod
    def on_close(ws):
        print "### closed ###"

    @staticmethod
    def on_open(ws):
        ws.send("Hello %d" % i)

Но у вас нет доступа к себе

person Bast    schedule 04.11.2018