如何给class方法装饰器的self参数添加类型注解?
How to add type annotation to self parameter of the decorator of class method?
所以,我有一个像这样的class装饰器
def something_todo(f):
@functools.wraps(f)
def decorator(self, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
我想在装饰器中对 self
参数进行类型注释。但是,这样写是行不通的
def something_todo(f):
@functools.wraps(f)
def decorator(self: SomeClass, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
class SomeClass:
def __init__(self):
# init here
@something_todo
def some_method(self, *args, **kwargs):
# some process
如果我在不同的脚本中编写装饰器和 class,情况甚至会变得更糟。这很可能会发生
ImportError: cannot import name 'SomeClass' from partially initialized module 'someclass' (most likely due to a circular import)
我这样做的原因是为了清楚起见,以便人们知道我的装饰器仅用于 class 方法。此外,我可以在编辑器中轻松检查 class 的所有方法或属性,而无需打开包含 class.
的脚本
编辑
所以这是解决方案的回顾:
假设我有 2 个脚本。一个包含 class
,一个包含装饰器函数。例如,
# inside decorators.py
def something_todo(f):
@functools.wraps(f)
def decorator(self, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
和
# inside someclass.py
from decorators import something_todo
class SomeClass:
def __init__(self):
# init here
@something_todo
def some_method(self, *args, **kwargs):
# some process
如果我想在装饰器中为 self
参数添加类型注释,我不能只这样做
# inside decorators.py
from someclass import SomeClass
def something_todo(f):
@functools.wraps(f)
def decorator(self: SomeClass, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
因为当我试图在另一个脚本中导入 class 时它会引发 ImportError
。所以,为了防止错误,我可以这样做
# inside decorators.py
from someclass import *
def something_todo(f):
@functools.wraps(f)
def decorator(self: "SomeClass", *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
而且效果很好。
两个有用的技巧:
- 前向声明类型为字符串文字,例如
self: 'SomeClass'
- 使用 callable protocols 更灵活地定义可调用
TypeVar
s。
import functools
from typing import cast, Any, Callable, Protocol, TypeVar
class SomeMethod(Protocol):
def __call__(
_self,
self: 'SomeClass',
*args: Any,
**kwargs: Any
) -> Any: ...
_SomeMethod = TypeVar("_SomeMethod", bound=SomeMethod)
def something_todo(f: _SomeMethod) -> _SomeMethod:
@functools.wraps(f)
def wrapper(self: SomeClass, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return cast(_SomeMethod, wrapper)
class SomeClass:
def __init__(self):
# init here
pass
@something_todo
def some_method(self, *args, **kwargs):
# some process
pass
@something_todo
def foo():
print('error: Value of type variable "_SomeMethod" of "something_todo" cannot be "Callable[[], None]"')
您没有 return 装饰函数。尝试添加行 return decorator
。如果我正确理解你的问题,那么实施没有任何问题。
from functools import wraps
def something_todo(f):
@wraps(f)
def decorator(self, *args, **kwargs):
# do something
print("test decorator")
return f(self, *args, **kwargs)
return decorator
class SomeClass:
def __init__(self):
pass
@something_todo
def some_method(self, *args, **kwargs):
print("test some method")
SomeClass().some_method()
打印:
test decorator
test some method
所以,我有一个像这样的class装饰器
def something_todo(f):
@functools.wraps(f)
def decorator(self, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
我想在装饰器中对 self
参数进行类型注释。但是,这样写是行不通的
def something_todo(f):
@functools.wraps(f)
def decorator(self: SomeClass, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
class SomeClass:
def __init__(self):
# init here
@something_todo
def some_method(self, *args, **kwargs):
# some process
如果我在不同的脚本中编写装饰器和 class,情况甚至会变得更糟。这很可能会发生
ImportError: cannot import name 'SomeClass' from partially initialized module 'someclass' (most likely due to a circular import)
我这样做的原因是为了清楚起见,以便人们知道我的装饰器仅用于 class 方法。此外,我可以在编辑器中轻松检查 class 的所有方法或属性,而无需打开包含 class.
的脚本编辑
所以这是解决方案的回顾:
假设我有 2 个脚本。一个包含 class
,一个包含装饰器函数。例如,
# inside decorators.py
def something_todo(f):
@functools.wraps(f)
def decorator(self, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
和
# inside someclass.py
from decorators import something_todo
class SomeClass:
def __init__(self):
# init here
@something_todo
def some_method(self, *args, **kwargs):
# some process
如果我想在装饰器中为 self
参数添加类型注释,我不能只这样做
# inside decorators.py
from someclass import SomeClass
def something_todo(f):
@functools.wraps(f)
def decorator(self: SomeClass, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
因为当我试图在另一个脚本中导入 class 时它会引发 ImportError
。所以,为了防止错误,我可以这样做
# inside decorators.py
from someclass import *
def something_todo(f):
@functools.wraps(f)
def decorator(self: "SomeClass", *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return decorator
而且效果很好。
两个有用的技巧:
- 前向声明类型为字符串文字,例如
self: 'SomeClass'
- 使用 callable protocols 更灵活地定义可调用
TypeVar
s。
import functools
from typing import cast, Any, Callable, Protocol, TypeVar
class SomeMethod(Protocol):
def __call__(
_self,
self: 'SomeClass',
*args: Any,
**kwargs: Any
) -> Any: ...
_SomeMethod = TypeVar("_SomeMethod", bound=SomeMethod)
def something_todo(f: _SomeMethod) -> _SomeMethod:
@functools.wraps(f)
def wrapper(self: SomeClass, *args, **kwargs):
# do something
return f(self, *args, **kwargs)
return cast(_SomeMethod, wrapper)
class SomeClass:
def __init__(self):
# init here
pass
@something_todo
def some_method(self, *args, **kwargs):
# some process
pass
@something_todo
def foo():
print('error: Value of type variable "_SomeMethod" of "something_todo" cannot be "Callable[[], None]"')
您没有 return 装饰函数。尝试添加行 return decorator
。如果我正确理解你的问题,那么实施没有任何问题。
from functools import wraps
def something_todo(f):
@wraps(f)
def decorator(self, *args, **kwargs):
# do something
print("test decorator")
return f(self, *args, **kwargs)
return decorator
class SomeClass:
def __init__(self):
pass
@something_todo
def some_method(self, *args, **kwargs):
print("test some method")
SomeClass().some_method()
打印:
test decorator
test some method