请问:第三个为什么是no?怎么实现为yes,即调用时会经过limiter和action装饰器闭包部分的代码,像是直接调用那样,我确定我的装饰器顺序没有错误
from functools import wraps
from typing import Callable, Any, TypeVar
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
def limiter(times: int = 1):
def decorator(func):
if not hasattr(func, 'action'):
raise AttributeError(
"The function is missing the 'action' attribute."
" Ensure that the @action decorator is applied before @limiter."
)
@wraps(func)
async def wrapper(*args, **kwargs):
if times <= 0:
return 'too many request'
return await func(*args, **kwargs)
return wrapper
return decorator
class A:
def __init__(self):
self._actions = {}
def action(self, name: str) -> Callable[[DecoratedCallable], DecoratedCallable]:
def decorator(func: DecoratedCallable) -> DecoratedCallable:
@wraps(func)
async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)
wrapper.action = name
self._actions[name] = wrapper
return wrapper
return decorator
async def handle(self, action_name: str, *args, **kwargs) -> Any:
if action_name in self._actions:
return await self._actions[action_name](*args, **kwargs)
raise ValueError(f"Action '{action_name}' not found")
a = A()
@limiter(times=0)
@a.action(name="message")
async def message():
return 'hello'
@limiter(times=1)
@a.action(name="gift")
async def gift():
return 'hello'
import asyncio
async def main():
if await message() == 'too many request':
print('yes')
else:
print('no')
if await gift() == 'hello':
print('yes')
else:
print('no')
if await a.handle('message') == 'too many request':
print('yes')
else:
print('no')
if await a.handle('gift') == 'hello':
print('yes')
else:
print('no')
asyncio.run(main())
# yes
# yes
# no
# yes
把limiter并入类里,虽然我不是很想这样写,但是这样是我目前能想到的解决办法了
def limiter(self, times: int = 1):
def decorator(func):
if not hasattr(func, 'action'):
raise AttributeError(
"The function is missing the 'action' attribute."
" Ensure that the @action decorator is applied before @limiter."
)
action_name = func.action
print(action_name)
@wraps(func)
async def wrapper(*args, **kwargs):
if times <= 0:
return 'too many request'
return await func(*args, **kwargs)
self._actions[action_name] = wrapper
return wrapper
return decorator
@a.limiter(times=0)
@a.action(name="message")
async def message():
return 'hello'
这样使用
这个问题应该是装饰器顺序的问题. 按你的定义顺序limiter里的func=A.wrapper; A里的func=message.所以直接调用时候顺序是这样的: limiter.wrapper -> A.wrapper -> message
a._actions里存的是A.wrapper,所以你通过_actions调用,是从中间A.wrapper这一层开始调用的.
from functools import wraps
from typing import Callable, Any, TypeVar
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
def limiter(times: int = 1):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
if times <= 0:
return 'too many request'
return await func(*args, **kwargs)
return wrapper
return decorator
class A:
def __init__(self):
self._actions = {}
def action(self, name: str) -> Callable[[DecoratedCallable], DecoratedCallable]:
def decorator(func: DecoratedCallable) -> DecoratedCallable:
@wraps(func)
async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)
wrapper.action = name
self._actions[name] = wrapper
return wrapper
return decorator
async def handle(self, action_name: str, *args, **kwargs) -> Any:
if action_name in self._actions:
return await self._actions[action_name](*args, **kwargs)
raise ValueError(f"Action '{action_name}' not found")
a = A()
@a.action(name="message")
@limiter(times=0)
async def message():
return 'hello'
@a.action(name="gift")
@limiter(times=1)
async def gift():
return 'hello'
import asyncio
async def main():
if await message() == 'too many request':
print('yes')
else:
print('no')
if await gift() == 'hello':
print('yes')
else:
print('no')
if await a.handle('message') == 'too many request':
print('yes')
else:
print('no')
if await a.handle('gift') == 'hello':
print('yes')
else:
print('no')
asyncio.run(main())
# yes
# yes
# yes
# yes
谢谢大佬的回答,不过很遗憾这不是装饰器的顺序的问题
如果调换了顺序没有经过action进行的标注,就会诱发AttributeError
的异常
执行顺序是先limiter再action(这里指被包装的函数之前执行的装饰器部分),但是标注顺序却是先action再limiter的
def decorator_a(func):
def wrapper(*args, **kwargs):
print("Decorator A: Before calling the function")
result = func(*args, **kwargs)
print("Decorator A: After calling the function")
return result
return wrapper
def decorator_b(func):
def wrapper(*args, **kwargs):
print("Decorator B: Before calling the function")
result = func(*args, **kwargs)
print("Decorator B: After calling the function")
return result
return wrapper
@decorator_a
@decorator_b
def my_function():
print("Inside my_function")
# 调用被装饰的函数
my_function()
结果
Decorator A: Before calling the function
Decorator B: Before calling the function
Inside my_function
Decorator B: After calling the function
Decorator A: After calling the function
@半醒着的阳光: 这个调用顺序,我感觉就是包含的关系
@decorator_a
@decorator_b
def my_function():
就相当于
decorator_a(decorator_b(my_function))
你得基于这个去设计,这样在b里是没办法调用到a的.
@半醒着的阳光: 我给你改了一个版本,在limiter里覆盖action,你试试看
from functools import wraps
from typing import Callable, Any, TypeVar
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
def limiter(times: int = 1):
def decorator(func):
if not hasattr(func, 'action'):
raise AttributeError(
"The function is missing the 'action' attribute."
" Ensure that the @action decorator is applied before @limiter."
)
@wraps(func)
async def wrapper(*args, **kwargs):
if times <= 0:
return 'too many request'
return await func(*args, **kwargs)
func.call = wrapper
return wrapper
return decorator
class A:
def __init__(self):
self._actions = {}
def action(self, name: str) -> Callable[[DecoratedCallable], DecoratedCallable]:
def decorator(func: DecoratedCallable) -> DecoratedCallable:
@wraps(func)
async def wrapper(*args, **kwargs):
return await func(*args, **kwargs)
wrapper.action = name
wrapper.call = wrapper
self._actions[name] = wrapper
return wrapper
return decorator
async def handle(self, action_name: str, *args, **kwargs) -> Any:
if action_name in self._actions:
return await self._actions[action_name].call(*args, **kwargs)
raise ValueError(f"Action '{action_name}' not found")
a = A()
@limiter(times=0)
@a.action(name="message")
async def message():
return 'hello'
@limiter(times=1)
@a.action(name="gift")
async def gift():
return 'hello'
import asyncio
async def main():
if await message() == 'too many request':
print('yes')
else:
print('no')
if await gift() == 'hello':
print('yes')
else:
print('no')
if await a.handle('message') == 'too many request':
print('yes')
else:
print('no')
if await a.handle('gift') == 'hello':
print('yes')
else:
print('no')
asyncio.run(main())
# yes
# yes
# yes
# yes
@www378660084: 大佬🐂🍺,这个方法好,这样我不用写到类里面了