在Python中,如何获取扩展类元素以自动完成显示?

时间:2018-07-18 11:32:13

标签: python pycharm

我是班级的新手,因此使用一个我在网上找到的示例来添加一些自定义日志记录级别。这将包含在一个库中,该库将导入到各种脚本中。它按预期工作,但添加的级别未显示在自动完成列表中(使用PyCharm),并且PyCharm抱怨LOGGER中有未解决的属性引用。当我编码时,输入“ LOGGER”。我看到正常的错误,警告,信息等可供选择,但我的自定义级别“详细”不在列表中。随着时间的流逝,将会添加更多的自定义级别,并且还将向一组开发人员推出,因此我需要进行这项工作。

有人知道为什么我的自动填充列表中没有冗长的选项吗?

enter image description here enter image description here

这是我的文件。

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)

my_script.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

2 个答案:

答案 0 :(得分:4)

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

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

告诉PyCharm类型检查器LOGGERPxLogger的实例的最简单方法是分配期间代码中的类型注释。 仅适用于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 2and3存根文件方法需要在程序包中实际模块文件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')

自动完成功能在以下三种方法中均能很好地发挥作用:

Autocomplete

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

注意: 在此答案的先前版本中,我说过docstring方法虽然显示了PxLogger类的自定义verbose()方法,但缺少了基类的方法。那是因为您从logging.getLoggerClass返回的值(即任意类)派生PxLogger。 如果将PxLogger明确地设为logging.Logger的子类,则类型检查器将知道基类,并可以正确地解析其方法以实现自动完成。

无论如何,我不建议对getLoggerClass的返回值进行子类化。您将要确定自己从中得到什么,而不要依赖返回正确结果的函数。

答案 1 :(得分:1)

通过键入注释变量。

LOGGER: PxLogger = logging.getLogger(__name__)

编辑:

以上解决方案适用于Python 3.6+。如@pLOPeGG在评论中指出的那样,对先前版本使用# type: type注释。

LOGGER = logging.getLogger(__name__)  # type: PxLogger