如何使用 Python @singledispatch 注册 typing.Callable?
How to register typing.Callable with Python @singledispatch?
背景
假设我要实现一个简单的装饰器 @notifyme
,它在调用装饰函数时打印一条消息。我希望装饰器接受一个参数来打印自定义消息;参数 (连同参数周围的括号) 可以省略,在这种情况下打印默认消息:
@notifyme('Foo is invoked!')
def foo():
pass
@notifyme # instead of @notifyme()
def bar():
pass
为了省略括号,我必须提供 @notifyme
的两个实现:
第一个实现允许用户自定义消息,因此它接受一个字符串作为参数和 returns 一个装饰器:
def notifyme_customized(message: str) -> Callable[[Callable], Callable]:
def decorator(func: Callable) -> Callable:
def decorated_func(*args, **kwargs):
print(str)
return func(*args, **kwargs)
return decorated_func
return decorator
第二个实现本身是装饰器,并使用第一个实现打印默认消息:
def notifyme_default(func: Callable) -> Callable:
return notifyme_customized('The function is invoked.')(func)
为了使上面的两个实现使用相同的名称 notifyme
,我使用 functools.singledispatch
将对 notifyme
的调用动态分派给两个实现之一:
# This is a complete minimal reproducible example
from functools import singledispatch
from typing import Callable
@singledispatch
def notifyme(arg):
return NotImplemented
@notifyme.register
def notifyme_customized(message: str) -> Callable[[Callable], Callable]:
def decorator(func: Callable) -> Callable:
def decorated_func(*args, **kwargs):
print(str)
return func(*args, **kwargs)
return decorated_func
return decorator
@notifyme.register
def notifyme_default(func: Callable) -> Callable:
return notifyme_customized('The function is invoked.')(func)
问题
但是,由于代码由 Python 解释器解释,它会抱怨 typing.Callable
是无效类型:
Traceback (most recent call last):
File "demo.py", line 20, in <module>
def notifyme_default(func: Callable) -> Callable:
File "C:\Program Files\Python38\lib\functools.py", line 860, in register
raise TypeError(
TypeError: Invalid annotation for 'func'. typing.Callable is not a class.
我在 Python bug 跟踪器上发现了 this issue,据此它似乎是自 Python 3.7 以来的预期行为。我目前使用的 Python 3.8(或最近发布的 Python 3.9)中是否存在解决方案或解决方法?
提前致谢。
我无法将 typing.Callable
与 functools.singledispatch
一起使用,但我确实通过使用 function
class 参考找到了解决方法:
from functools import singledispatch
from typing import Callable
function = type(lambda: ())
@singledispatch
def notifyme(arg):
return NotImplemented
@notifyme.register
def notifyme_customized(message: str) -> Callable[[Callable], Callable]:
def decorator(func: Callable) -> Callable:
def decorated_func(*args, **kwargs):
print(str)
return func(*args, **kwargs)
return decorated_func
return decorator
@notifyme.register
def notifyme_default(func: function) -> Callable:
return notifyme_customized('The function is invoked.')(func)
https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable
from collections import abc
@notifyme.register
def notifyme_default(func: abc.Callable) -> Callable:
return notifyme_customized('The function is invoked.')(func)
背景
假设我要实现一个简单的装饰器 @notifyme
,它在调用装饰函数时打印一条消息。我希望装饰器接受一个参数来打印自定义消息;参数 (连同参数周围的括号) 可以省略,在这种情况下打印默认消息:
@notifyme('Foo is invoked!')
def foo():
pass
@notifyme # instead of @notifyme()
def bar():
pass
为了省略括号,我必须提供 @notifyme
的两个实现:
第一个实现允许用户自定义消息,因此它接受一个字符串作为参数和 returns 一个装饰器:
def notifyme_customized(message: str) -> Callable[[Callable], Callable]: def decorator(func: Callable) -> Callable: def decorated_func(*args, **kwargs): print(str) return func(*args, **kwargs) return decorated_func return decorator
第二个实现本身是装饰器,并使用第一个实现打印默认消息:
def notifyme_default(func: Callable) -> Callable: return notifyme_customized('The function is invoked.')(func)
为了使上面的两个实现使用相同的名称 notifyme
,我使用 functools.singledispatch
将对 notifyme
的调用动态分派给两个实现之一:
# This is a complete minimal reproducible example
from functools import singledispatch
from typing import Callable
@singledispatch
def notifyme(arg):
return NotImplemented
@notifyme.register
def notifyme_customized(message: str) -> Callable[[Callable], Callable]:
def decorator(func: Callable) -> Callable:
def decorated_func(*args, **kwargs):
print(str)
return func(*args, **kwargs)
return decorated_func
return decorator
@notifyme.register
def notifyme_default(func: Callable) -> Callable:
return notifyme_customized('The function is invoked.')(func)
问题
但是,由于代码由 Python 解释器解释,它会抱怨 typing.Callable
是无效类型:
Traceback (most recent call last):
File "demo.py", line 20, in <module>
def notifyme_default(func: Callable) -> Callable:
File "C:\Program Files\Python38\lib\functools.py", line 860, in register
raise TypeError(
TypeError: Invalid annotation for 'func'. typing.Callable is not a class.
我在 Python bug 跟踪器上发现了 this issue,据此它似乎是自 Python 3.7 以来的预期行为。我目前使用的 Python 3.8(或最近发布的 Python 3.9)中是否存在解决方案或解决方法?
提前致谢。
我无法将 typing.Callable
与 functools.singledispatch
一起使用,但我确实通过使用 function
class 参考找到了解决方法:
from functools import singledispatch
from typing import Callable
function = type(lambda: ())
@singledispatch
def notifyme(arg):
return NotImplemented
@notifyme.register
def notifyme_customized(message: str) -> Callable[[Callable], Callable]:
def decorator(func: Callable) -> Callable:
def decorated_func(*args, **kwargs):
print(str)
return func(*args, **kwargs)
return decorated_func
return decorator
@notifyme.register
def notifyme_default(func: function) -> Callable:
return notifyme_customized('The function is invoked.')(func)
https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable
from collections import abc
@notifyme.register
def notifyme_default(func: abc.Callable) -> Callable:
return notifyme_customized('The function is invoked.')(func)