Skip to main content
 首页 » 编程设计

python之在 Python 中,如何让扩展类元素显示在自动完成中

2025年05月04日19kenshinobiy

我是新手,所以使用我在网上找到的示例来添加一些自定义日志记录级别。这将包含在一个库中,该库将导入到各种脚本中。它按预期工作,但添加的级别未显示在自动完成列表中(使用 PyCharm),并且 PyCharm 提示 LOGGER 中存在未解析的属性引用。当我编码并输入“LOGGER”时。我看到可供选择的正常错误、警告、信息等,但我的自定义级别“详细”不在列表中。随着时间的推移,将会添加更多的自定义关卡,这也将被推广到一个开发团队,所以我需要让它发挥作用。

知道为什么 verbose 不是我的自动完成列表中的选项吗?

这是我的文件。

px_logger.py

from logging import getLoggerClass, addLevelName, setLoggerClass, NOTSET 
 
public class PxLogger(getLoggerClass()): 
    def __init__(self, name, level=NOTSET): 
        super(PxLogger, self).__init__(name, level) 
 
        addLevelName(5, "VERBOSE") 
 
    def verbose(self, msg, *args, **kwargs): 
        """Custom logger level - verbose""" 
        if self.isEnabledFor(5): 
            self._log(5, msg, args, **kwargs) 

我的脚本.py

import json 
import logging.config 
from px_logger import PxLogger 
 
logging.setLoggerClass(PxLogger) 
LOGGER = logging.getLogger(__name__) 
 
with open('../logging.json') as f:  # load logging config file 
    CONFIG_DICT = json.load(f) 
logging.config.dictConfig(CONFIG_DICT) 
 
LOGGER.verbose('Test verbose message') 

屏幕输出

VERBOSE - Test verbose message 

请您参考如下方法:

PyCharm 提供了多种方法来完成 type hinting

在内部,它使用 Typeshed确定标准库对象和常见的第 3 方包的类型。 这也是 PyCharm 获取 logging.getLogger 返回值类型的地方。这就是为什么它不在自动完成中显示子类的 verbose 方法的原因,因为它假定 LOGGER 是 logging.Logger 的实例。

告诉 PyCharm 的类型检查器 LOGGER 是 PxLogger 的实例的最简单方法是在赋值期间在代码中添加类型注释。 这仅适用于 Python 3.5+:

LOGGER: PxLogger = logging.getLogger(__name__) 

如果您更进一步,您将封装自定义记录器类的定义,它被分配为全局记录器类,并在您的模块中定义 logging.getLogger 的包装器。

这将使您的同事只需导入您的模块而不是 logging 并像使用原始 logging 一样使用它,而不必担心要设置哪个类作为记录器类或如何注释保存其记录器的变量。

沿着这条路走下去时,有三个选项可以为类型检查器包含类型提示。

px_logger.py

# basically, import from logging whatever you may need and overwrite where necessary 
from logging import getLogger as _getLogger, Logger, addLevelName, setLoggerClass, NOTSET 
from typing import Optional # this only for the Python 3.5+ solution 
 
class PxLogger(Logger):  # Note: subclass logging.Logger explicitly 
    def __init__(self, name, level=NOTSET): 
        super(PxLogger, self).__init__(name, level) 
 
        addLevelName(5, "VERBOSE") 
 
    def verbose(self, msg, *args, **kwargs): 
        """Custom logger level - verbose""" 
        if self.isEnabledFor(5): 
            self._log(5, msg, args, **kwargs) 
 
setLoggerClass(PxLogger) 
 
""" 
Possible approaches, implement one of the below. 
The first is Python 3.5+ only. 
The second and third work for both, Python 2 and Python 3. 
""" 
# using type annotation syntax (py35+) 
def getLogger(name: Optional[str]=None) -> PxLogger: 
    _logr: PxLogger = _getLogger(name) 
    return _logr 
 
# using (legacy) docstring syntax (py2and3) 
def getLogger(name=None) 
    """ 
    :param name: str 
    :rtype: PxLogger 
    """  
    return _getLogger(name) 
 
# using a stub file (py2and3) 
def getLogger(name=None): 
    return _getLogger(name) 

Python 2 和 3 stub 文件方法需要一个名为 py_logger.pyi 的文件,它位于包中的实际模块文件 px_logger.py 旁边。

px_logger.pyi

# The PEP-484 syntax does not matter here.  
# The Python interpreter will ignore this file,  
# it is only relevant for the static type checker 
import logging 
 
class PxLogger(logging.Logger): 
    def verbose(self, msg, *args, **kwargs) -> None: ... 
 
def getLogger(name) -> PxLogger: ... 

对于所有三种方法,您的模块 my_script 看起来都一样:

my_script.py

import logging.config 
import px_logger 
 
LOGGER = px_logger.getLogger(__name__) 
 
# I used basicConfig here for simplicity, dictConfig should work just as well 
logging.basicConfig(level=5, 
                    format='%(asctime)s - %(levelname)s [%(filename)s]: %(name)s %(funcName)20s - Message: %(message)s', 
                    datefmt='%d.%m.%Y %H:%M:%S', 
                    filename='myapp.log', 
                    filemode='a') 
 
LOGGER.verbose('Test verbose message') 

自动完成适用于所有三种方法:

方法二和方法三已在 PyCharm 2018.1 CE 中使用 Python 2.7.15 和 3.6.5 进行了测试

注意: 在此答案的先前修订中,我指出文档字符串方法虽然显示了 PxLogger 类的自定义 verbose() 方法,但缺少基类的方法。这是因为您从 logging.getLoggerClass 返回的任何内容派生 PxLogger,即任意类。 如果您明确地将 PxLogger 设为 logging.Logger 的子类,则类型检查器会知道基类并可以正确解析其自动完成方法。

无论如何,我不建议对 getLoggerClass 的返回值进行子类化。您需要确定从中得到什么,而不是依赖于返回正确内容的函数。