简体   繁体   English

如何使用 Python @singledispatch 注册 Typing.Callable?

[英]How to register typing.Callable with Python @singledispatch?

Background背景

Suppose I am to implement a simple decorator @notifyme that prints a message when the decorated function is invoked.假设我要实现一个简单的装饰器@notifyme ,它会在调用装饰函数时打印一条消息。 I would like the decorator to accept one argument to print a customized message;我希望装饰器接受一个参数来打印自定义消息; the argument (along with the parentheses surrounding the argument) may be omitted, in which case the default message is printed:可以省略参数(以及围绕参数的括号) ,在这种情况下,将打印默认消息:

@notifyme('Foo is invoked!')
def foo():
    pass

@notifyme  # instead of @notifyme()
def bar():
    pass

To allow the parentheses to be omitted, I have to provide two implementations of @notifyme :为了允许省略括号,我必须提供@notifyme两种实现:

  1. The first implementation allows the user to customize the message, so it accepts a string as argument and returns a decorator:第一个实现允许用户自定义消息,因此它接受一个字符串作为参数并返回一个装饰器:

     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
  2. The second implementation is a decorator itself and uses the first implementation to print a default message:第二个实现是装饰器本身,并使用第一个实现打印默认消息:

     def notifyme_default(func: Callable) -> Callable: return notifyme_customized('The function is invoked.')(func)

To make the two implementations above use the same name notifyme , I used functools.singledispatch to dynamically dispatch the call to notifyme to one of the two implementations:为了使上面的两个实现使用相同的名称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)

Problem问题

However, as the code is interpreted by the Python interpreter, it complains that typing.Callable is an invalid type:但是,当 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.

I have found this issue on Python bug tracker, according to which it seems to be expected behavior since Python 3.7.我在 Python 错误跟踪器上发现了这个问题,根据它似乎是自 Python 3.7 以来的预期行为。 Does a solution or workaround exist in Python 3.8 I use currently (or Python 3.9 that has been released recently)?我当前使用的 Python 3.8(或最近发布的 Python 3.9)中是否存在解决方案或变通方法?

Thanks in advance.提前致谢。

https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable 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)

I was unable to use typing.Callable with functools.singledispatch , but I did find a workaround by using a function class reference instead:我无法将typing.Callablefunctools.singledispatch一起使用,但我确实通过使用function类引用找到了一种解决方法:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM