比如说,我有一个装饰器可以使日志记录更加简单:
from typing import TypeVar
_T = TypeVar("_T", bound=type)
def with_logger(cls: _T) -> _T:
from logging import getLogger
cls.logger = getLogger(cls.__name__)
return cls
@with_logger
class Spam:
def __init__(self) -> None:
print(f"{Spam.logger.name = }")
Spam()
代码运行良好。然而,PyCharm 和
mypy
都抱怨 Spam
没有名为 logger
的属性。引起警告的行是 print(f"{Spam.logger.name = }")
。
在这个问题之后,我将代码转换为以下可怕的代码:
from typing import TypeVar
_T = TypeVar("_T", bound=type)
def with_logger(cls: _T) -> _T:
from logging import getLogger
from typing import TYPE_CHECKING
setattr(cls, 'logger', getLogger(cls.__name__))
if TYPE_CHECKING:
from typing import ClassVar, Protocol, cast
class LoggerMixin(Protocol):
from logging import Logger
logger: ClassVar[Logger] = getLogger(cls.__name__)
return cast(_T, type(cls.__name__, (LoggerMixin, cls), dict()))
else:
return cls
@with_logger
class Spam:
def __init__(self) -> None:
print(f"{Spam.logger.name = }")
Spam()
还是没用。 PyCharm、
mypy
和 basedmypy
声称 logger
尚未解决。
还有什么可做的吗?距离提问已经一年多了。
我可以在每个装饰类中显式键入
logger
,但解决方案就是完全丢弃装饰器:
from logging import Logger, getLogger
from typing import ClassVar, TypeVar
_T = TypeVar("_T", bound=type)
def with_logger(cls: _T) -> _T:
setattr(cls, 'logger', getLogger(cls.__name__))
return cls
@with_logger
class Spam:
logger: ClassVar[Logger]
def __init__(self) -> None:
print(f"{Spam.logger.name = }")
Spam()
正如@KamilCuk建议的那样,一个可行的方法是继承一个创建
logger
的类:
from logging import Logger, getLogger
from typing import ClassVar
class BaseLogger:
logger: ClassVar[Logger]
def __new__(cls, *args, **kwargs):
cls.logger = getLogger(cls.__name__)
return super().__new__(cls)
class Spam(BaseLogger):
def __init__(self) -> None:
print(f"{Spam.logger.name = }")
Spam()
效果很好。它在 PyCharm 中正确突出显示。我只是想知道是否可以通过装饰器来完成。
basedmypy
还是不开心。
只是超正常的继承。一些事情:
from logging import Logger, getLogger
class BaseLogger:
@property
@classmethod
def logger(cls):
return getLogger(cls.__name__)
class Spam(BaseLogger):
def __init__(self) -> None:
print(f"{Spam.logger.name = }")