半醒着的阳光 2024-10-31 09:15
浏览 2
已结题

python装饰器调用问题

问题: 为什么下面的代码使用connect 根据_actions触发对应的函数无法经过装饰器内部的wrapper ,而直接调用的direct却能够经过装饰器内部的wrapper ,请问怎么解决这个问题

  1. 我确保在程序能够正确的注册send_message到_actions中去。你可以看到我能够调用send_message而不报错
  2. 我使用的python版本是3.11,操作系统为win10
from functools import wraps
from typing import Callable, Optional


class ActionConsumerMeta(type):
    """
    记录操作方法的元类
    Metaclass that records action methods
    """

    def __new__(mcs, name, bases, attrs):
        cls = super().__new__(mcs, name, bases, attrs)

        cls._actions = {}

        for attr_name, attr_value in attrs.items():  # 函数名、函数
            if hasattr(attr_value, "action"):
                action_name = attr_value.action
                cls._actions[action_name] = attr_name  # 返回action对应的函数名 # 这一步是为了以类的方式调用一个独立的action装饰器,这会在元类操作之前,与主要问题没有关系
        return cls


data: dict = {
    "type": "request",
    "action": "message",
    "data": {
        "message": None,
        "additional_info": "Any other info"
    },
    "from_": "1",
    "to": "server",
    "request_id": 1
}
websocket = None
channel = '1231'

class GroupChatRoom(metaclass=ActionConsumerMeta):
    def action(self, action_name):
        def decorator(func):
            func.action = (action_name, None)
            self._actions[action_name] = func

            @wraps(func)
            async def wrapper(*args, **kwargs):
                print(f"Entering action for action: {action_name}")
                return await func(*args, **kwargs)

            return wrapper

        return decorator


class GroupChatChannelRoom(GroupChatRoom):
    async def direct(self):
        print(send_message)  # <function send_message at 0x00000277CD990900>
        await send_message(websocket, channel, data=data)

    async def connect(self):
        await self._receiver(websocket=websocket, channel=channel)

    # async def _receiver(self, websocket: WebSocket, channel: str):
    async def _receiver(self, websocket, channel: str):
        """接收信息"""
        action = data.get('action')
        action_func_or_str: str | Callable = self._actions.get(action)
        print(action_func_or_str)  # <function send_message at 0x000001D6EF0609A0>
        if isinstance(action_func_or_str, str):
            await getattr(self, action_func_or_str)(websocket=websocket, channel=channel, data=data)
        else:
            await action_func_or_str(websocket=websocket, channel=channel, data=data)


group_chatroom = GroupChatChannelRoom()

@group_chatroom.action('message')
async def send_message(websocket, channel: str, data: dict, **kwargs):
    print(f"Sending message to channel {channel}: {data.get('message')}")


import asyncio


async def a():
    print('connect')
    await group_chatroom.connect()
    # Sending message to channel 123: None

    print('direct')
    await group_chatroom.direct()
    # Entering action for action: message
    # Sending message to channel 1231: None


asyncio.run(a())
  • 写回答

1条回答 默认 最新

  • 半醒着的阳光 2024-10-31 09:22
    关注

    应当在装饰器中给_actions添加对应的action把包装好的wrapper

    from functools import wraps
    from typing import Callable
    
    class ActionConsumerMeta(type):
        def __new__(mcs, name, bases, attrs):
            cls = super().__new__(mcs, name, bases, attrs)
            cls._actions = {}
            for attr_name, attr_value in attrs.items():
                if hasattr(attr_value, "action"):
                    action_name = attr_value.action
                    cls._actions[action_name] = attr_value
            return cls
    
    data = {
        "type": "request",
        "action": "message",
        "data": {
            "message": None,
            "additional_info": "Any other info"
        },
        "from_": "1",
        "to": "server",
        "request_id": 1
    }
    websocket = None
    channel = '1231'
    
    class GroupChatRoom(metaclass=ActionConsumerMeta):
        def action(self, action_name):
            def decorator(func):
                @wraps(func)
                async def wrapper(*args, **kwargs):
                    print(f"Entering action for action: {action_name}")
                    return await func(*args, **kwargs)
                
                wrapper.action = action_name
                self._actions[action_name] = wrapper
                return wrapper
    
            return decorator
    
    class GroupChatChannelRoom(GroupChatRoom):
        async def direct(self):
            await send_message(websocket, channel, data=data)
    
        async def connect(self):
            await self._receiver(websocket=websocket, channel=channel)
    
        async def _receiver(self, websocket, channel: str):
            action = data.get('action')
            action_func = self._actions.get(action)
            if action_func:
                await action_func(websocket=websocket, channel=channel, data=data)
    
    group_chatroom = GroupChatChannelRoom()
    
    @group_chatroom.action('message')
    async def send_message(websocket, channel: str, data: dict, **kwargs):
        print(f"Sending message to channel {channel}: {data.get('message')}")
    
    import asyncio
    
    async def a():
        print('connect')
        await group_chatroom.connect()
        print('direct')
        await group_chatroom.direct()
    
    asyncio.run(a())
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 11月8日
  • 已采纳回答 10月31日
  • 创建了问题 10月31日