admin管理员组

文章数量:1332881

I am trying to implement a decorator looks like

def decorator(cls):
    class _Wrapper(cls):
        def func(self):
            super().func()
            print("patched!")
    return _Wrapper

and I am wondering how do I hint the cls and the return value type. I tried to use _Class=TypeVar("_Class") and cls: Type[_Class] but mypy complained that value cls is not valid as a type. Also I do not know how to hint the return type.

I am trying to implement a decorator looks like

def decorator(cls):
    class _Wrapper(cls):
        def func(self):
            super().func()
            print("patched!")
    return _Wrapper

and I am wondering how do I hint the cls and the return value type. I tried to use _Class=TypeVar("_Class") and cls: Type[_Class] but mypy complained that value cls is not valid as a type. Also I do not know how to hint the return type.

Share Improve this question edited Nov 21, 2024 at 7:15 InSync 10.9k4 gold badges17 silver badges56 bronze badges asked Nov 21, 2024 at 6:03 xisxis 24.9k10 gold badges46 silver badges61 bronze badges 6
  • Duplicates: Python type hint for (any) class, Python Typing: declare return value type based on function argument. If these do not answer your problem, please edit to clarify. – InSync Commented Nov 21, 2024 at 10:44
  • @InSync While annotating the decorator to take a type argument and return a type value may silence MyPy, its also not entirely correct as you want the decorator to take an argument that is "any class with the func method" and return the same (otherwise you could wrap the decorator around a class without the func method and MyPy would not complain). – MT0 Commented Nov 21, 2024 at 11:28
  • @MT0 We have a dupe for that too: Type hint for an object that has a given attribute?. – InSync Commented Nov 21, 2024 at 12:17
  • @InSync That's a better duplicate but its still not perfect as there is currently a bug in MyPy so even if you implement the Protocol and use the correct type hints (combining both type and Protocol) it still won't (in the specific situation the OP is using) silence all the errors from the type checker. – MT0 Commented Nov 21, 2024 at 12:23
  • If you know the class you are defining will have a func method, wouldn't it be simpler to decorate that method instead of defining an unnecessary subclass? – chepner Commented Nov 24, 2024 at 15:16
 |  Show 1 more comment

2 Answers 2

Reset to default 1

You should be able to use a Protocol:

from __future__ import annotations

from typing import Protocol


class SupportsFunc(Protocol):
    def func(self) -> None: ...


def decorator(cls: type[SupportsFunc]) -> type[SupportsFunc]:
    class _Wrapper(cls):
        def func(self) -> None:
            super().func()
            print("patched!")
    return _Wrapper


@decorator
class Test:
    def func(self) -> None:
        print("original")

instance = Test()
instance.func()

However, MyPy will generate an error for the line class _Wrapper(cls)::

main.py:9: error: Variable "cls" is not valid as a type  [valid-type]
main.py:9: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:9: error: Invalid base class "cls"  [misc]
Found 2 errors in 1 file (checked 1 source file)

This appears to be an open bug on MyPy and you may need to comment out those errors until it is fixed.

def decorator(cls: type[SupportsFunc]) -> type[SupportsFunc]:
    class _Wrapper(cls):  # type: ignore[valid-type, misc]  # See https://github/python/mypy/issues/14458
        def func(self) -> None:
            super().func()
            print("patched!")
    return _Wrapper

The type of class is "type":

class new:
    pass

print(type(new))

output:

<class 'type'>

you can set:

def decorator(cls: type) -> type:
class _Wrapper(cls):
    def func(self):
        super().func()
        print("patched!")
return _Wrapper

Or you can define a custom type name, for example:

type _claas = type

def decorator(cls: _claas) -> _claas:
    class _Wrapper(cls):
        def func(self):
            super().func()
            print("patched!")
    return _Wrapper

本文标签: