如何为带有类型提示的输入函数编写Python装饰器?

huangapple go评论56阅读模式
英文:

How can I write a python decorator for a input function with type hinting?

问题

I'm trying to create the following python decorator for a function with type hint but it doesn't seem to work. It returned None instead of True. What did I do wrong?

def do_nothing(func):
    def Inner_Function(*args, **kwargs):
        func(*args, **kwargs)
    return Inner_Function

@do_nothing
def is_cold(name: str) -> bool:
    return True

g = is_cold("hi")
print(g)
# >> None
英文:

I'm trying create the following python decorator for a function with type hint but it doesn't seem to work. It returned None instead of True. What did I do wrong?

    def do_nothing(func):
        def Inner_Function(*args, **kwargs):
            func(*args, **kwargs)
        return Inner_Function
    @do_nothing
    def is_cold(name: str) -> bool:
        return True
    
    g = is_cold("hi")
    print(g)
>> None

答案1

得分: 1

要修复你的装饰器的实际运行行为,内部函数需要return原始函数的调用结果:

def do_nothing(func):
    def Inner_Function(*args, **kwargs):
        return func(*args, **kwargs)
    return Inner_Function

@do_nothing
def is_cold(name: str) -> bool:
    return True

g = is_cold("hi")
print(g)  # True

如果你正在使用类型检查,你会注意到这个装饰器会破坏被装饰函数的类型。如果我们在mypy中检查g的类型,我们会看到它是Any

g = is_cold("hi")
reveal_type(g)  # Revealed type is "Any"

为了解决这个问题,你需要为你的装饰器添加类型提示,以指示它接受一个函数作为参数,并返回相同类型的函数

from typing import Callable, TypeVar

_Func = TypeVar("_Func", bound=Callable)

def do_nothing(func: _Func) -> _Func:
    def Inner_Function(*args, **kwargs):
        return func(*args, **kwargs)
    return Inner_Function  # type: ignore

(你可以为Inner_Function做更复杂的类型提示,以消除# type: ignore的需要,但就@do_nothing装饰器的用户而言,内部发生的事情并不重要,只有do_nothing本身的签名才重要。)

现在is_cold的类型由装饰器保留:

g = is_cold("hi")
reveal_type(g)  # Revealed type is "builtins.bool"
英文:

To fix the actual runtime behavior of your decorator, the inner function needs to return the result of calling the original function:

def do_nothing(func):
    def Inner_Function(*args, **kwargs):
        return func(*args, **kwargs)
    return Inner_Function

@do_nothing
def is_cold(name: str) -> bool:
    return True

g = is_cold("hi")
print(g)  # True

If you're using typechecking, you'll notice that this decorator breaks the typing of the decorated function. If we check the type of g in mypy we'll see that it's Any:

g = is_cold("hi")
reveal_type(g)  # Revealed type is "Any"

To fix this, you need to add typing to your decorator to indicate that it takes a function as its argument and returns the same type of function:

from typing import Callable, TypeVar

_Func = TypeVar("_Func", bound=Callable)

def do_nothing(func: _Func) -> _Func:
    def Inner_Function(*args, **kwargs):
        return func(*args, **kwargs)
    return Inner_Function  # type: ignore

(You can do more complex typing for Inner_Function to eliminate the need for the # type: ignore, but as far as the users of the @do_nothing decorator are concerned, what happens inside doesn't matter, only the signature of do_nothing itself.)

Now is_cold has its type preserved by the decorator:

g = is_cold("hi")
reveal_type(g)  # Revealed type is "builtins.bool"

答案2

得分: 0

InnerFunction 调用 func 但没有返回任何内容。 is_cold 将返回 True,但然后 InnerFunction 将其丢弃,并默认返回 None

英文:

InnerFunction calls func but doesn't return anything. is_cold will return True, but then InnerFunction tosses that away and, by default, returns None.

huangapple
  • 本文由 发表于 2023年5月13日 09:44:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76240755.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定