如何轻松处理Python中的动态装饰器?

1 投票
1 回答
1682 浏览
提问于 2025-04-18 05:09

假设我有以下的设置(简化版):

from flask import Flask
from flask.ext.socketio import SocketIO, emit, send

app = Flask(__name__)
socketio = SocketIO(app)

@socketio.on('connect', namespace='/namespaceOne')
def test_connectOne():
    print('new connection')

@socketio.on('connect', namespace='/namespaceTwo')
def test_connectTwo():
    print('new connection')

if __name__ == '__main__':
socketio.run(app)

现在我想把两个函数 test_connectOnetest_ConnectTwo 移到不同的模块里。我在考虑为每个模块创建两个类,并把这些函数设为静态的,像这样:

class ModuleOne (object):

    @staticmethod
    @socketio.on('connect', namespace='/namespaceOne')
    def test_One():
        print('new connection')

但这样做我遇到了一些麻烦。我需要想办法把 socketio 对象放进类里(比如通过一个简单的设置方法和一个静态变量结合起来)。不过我想,注解 socketio.on() 会在模块 ModuleOne 被其他地方导入时就触发,而那时我还没来得及设置任何变量。

我该怎么解决这个问题呢?(我也欢迎一些完全不同的解决方案。)

1 个回答

4

你有两个选择:

  1. socketio 对象导入到你放置视图的单独模块中。只要你在创建 socketio 对象后再导入这个模块,事情就能正常运作。所以在你的 main 模块里,你可以这样写:

    from flask import Flask
    from flask.ext.socketio import SocketIO, emit, send
    
    app = Flask(__name__)
    socketio = SocketIO(app)
    
    import socket_views
    
    if __name__ == '__main__':
        socketio.run(app)
    

    而在 socket_views.py 里,你可以这样写:

    from main import socketio
    
    @socketio.on('connect', namespace='/namespaceOne')
    def test_connectOne():
        print('new connection')
    
    @socketio.on('connect', namespace='/namespaceTwo')
    def test_connectTwo():
        print('new connection')
    

    可以参考 Flask 文档中的 大型应用章节,特别是循环导入的部分。

  2. 手动使用 @socketio.on() 装饰器;@expression 这种写法只是让你更方便地把一个可调用的东西应用到一个函数上。因为 @socketio.on() 装饰器只是用来“注册”的,所以你可以把你的视图写成普通的、不带装饰器的函数,然后在导入后用下面的方式注册它们:

    from socket_views import test_connectOne, test_connectTwo
    
    socketio.on('connect', namespace='/namespaceOne')(test_connectOne)
    socketio.on('connect', namespace='/namespaceTwo')(test_connectTwo)
    

撰写回答