在类定义尚未完成时,Python不允许我在类中使用方法

时间:2010-12-29 17:30:50

标签: python oop class methods python-2.6

我使用Python 2.6作为批处理脚本替换。它将通过双击启动,因此用户将丢失/忽略对stdout的所有输出。所以,我决定添加日志记录,为了简单起见,我为此写了一个类。我的想法是,我可以在我的代码中的任何地方使用Logging.Logger,并且记录器可以随时使用。

我希望目录中的日志文件不超过10个,因此我手动清除旧的日志文件。我没有通过API找到这样的功能,而且我是偏执狂,并希望记录所有事件,事件是日志目录中有意外名称的文件。

所以,下面是我对这个类的尝试,但是当我尝试测试(运行)它时出现错误:

>>> ================================ RESTART ================================
>>> 

Traceback (most recent call last):
  File "C:/AutomationScripts/build scripts/Deleteme.py", line 6, in <module>
    class Logging:
  File "C:/AutomationScripts/build scripts/Deleteme.py", line 42, in Logging
    __clearOldLogs(dummySetting)
  File "C:/AutomationScripts/build scripts/Deleteme.py", line 38, in __clearOldLogs
    _assert(Logger, 'Logger does not exist yet! Why?')
NameError: global name '_assert' is not defined
>>> 

是的,我来自Java / C#背景。我可能不会做“Pythonic”的方式。请帮我做正确的事,请给出一个完整的答案,而不是简单地指出我的知识漏洞。我相信我提供了足够的代码示例。对不起,如果没有设置类,它就无法运行,但希望你明白了。

# This file has been tested on Python 2.6.*. For Windows only.

import logging          # For actual logging
import tkMessageBox     # To display an error (if logging is unavailable)

class Logging:
    """
    Logging class provides simplified interface to logging
    as well as provides some handy functions.
    """

    # To be set when the logger is properly configured.
    Logger = None

    @staticmethod
    def _assert(condition, message):
        """ Like a regular assert, except that it should be visible to the user. """
        if condition: return
        # Else log and fail
        if Logger:
            Logger.debug(traceback.format_stack())
            Logger.error('Assert failed: ' + message)
        else:
            tkMessageBox.showinfo('Assert failed: ' + message, traceback.format_stack())            
        assert(condition)

    @staticmethod
    def _removeFromEnd(string, endString):
        _assert(string.endswith(endString),
                "String '{0}' does not end in '{1}'.".format(string, endString))
        return string[:-len(endString)]

    def __clearOldLogs(logSettings):
        """
        We want to clear old (by date) files if we get too many.
        We should call this method only after variable 'Logger' has been created.
        """
        # The following check should not be necessary from outside of
        # Logging class, when it is fully defined
        _assert(Logger, 'Logger does not exist yet! Why?')
        # Do more work here

    def __createLogger(logSettings):
        logFileName = logSettings.LogFileNameFunc()
        #_assert(False, 'Test')
        logName = _removeFromEnd(logFileName, logSettings.FileExtension)
        logFileName = os.path.join(logSettings.BaseDir, logFileName)
        # If someone tried to log something before basicConfig is called,
        # Python creates a default handler that goes to the console and will
        # ignore further basicConfig calls. Remove the handler if there is one.
        root = logging.getLogger()
        if root.handlers:
            for handler in root.handlers:
                root.removeHandler(handler)
        logging.basicConfig(filename = logFileName, name = logName, level = logging.DEBUG, format = "%(asctime)s - %(levelname)s - %(message)s")
        logger = logging.getLogger(logName)
        return logger

    # Settings is a separate class (not dependent on this one).    
    Logger = __createLogger(Settings.LOGGING)
    __clearOldLogs(Settings.LOGGING)

if __name__ == '__main__':
    # This code section will not run when the class is imported.
    # If it is run directly, then we will print debugging info.
    logger = Logging.Logger
    logger.debug('Test debug message.')
    logger.info('Test info message.')
    logger.warning('Test warning message.')
    logger.error('Test error message.')
    logger.critical('Test critical message.')

欢迎提出相关问题,风格建议和完整答案。谢谢!

4 个答案:

答案 0 :(得分:5)

您收到该异常是因为您正在调用_assert()而不是Logging._assert()。错误消息告诉您它正在模块的全局命名空间而不是类命名空间中查找_assert();要让它查看后者,你必须明确指定它。

当然,在这种情况下,你正在尝试在类仍然被定义时这样做,并且在类完成之前名称不可用,因此要做到这一点很难。

解决方案是取消缩进以下两行(我已编辑为使用完全限定名),以便它们在类定义之外;它们将在它之后立即执行。

Logger = Logging.__createLogger(Settings.LOGGING)
Logging.__clearOldLogs(Settings.LOGGING)

一个有助于的样式建议:不要使用一堆静态方法创建一个类,而是考虑将它们作为模块中的顶级函数。模块的用户(包括您自己)会发现更容易获得他们想要的功能。没有理由将一个类用作容器;模块本身就是这样一个容器。

模块基本上是一个*.py文件(虽然您可以创建具有多个文件的模块,但现在这样做)。当您执行import时,您要导入的是一个模块。在您的示例中,tkMessageBoxlogging都是模块。因此,只需创建一个单独的文件(确保其名称与现有的Python模块名称不冲突),将其保存在与主脚本相同的目录中,然后将其导入主脚本中。如果您将其命名为mylogging.py,那么您将import mylogging并以mylogging.clearOldLogs()或其他方式访问其中的函数(类似于您现在作为一个类处理它们的方式)。

Python中的“全局”名称并不是真正的全局名称,它们只是全局到它们所定义的模块。因此,模块是划分功能的好方法,尤其是部分(比如日志记录)你预计会在很多未来的脚本中重复使用。

答案 1 :(得分:2)

替换

_assert(Logger, 'Logger does not exist yet! Why?')

Logging._assert(Logger, 'Logger does not exist yet! Why?')

这是因为您将_assert定义为类的静态方法,并且静态方法必须被称为ClassName.methodName,即使您从该类的实例的方法调用它也是如此

答案 2 :(得分:1)

我认为它只需要是Logging._assert。 Python不像java那样执行命名空间解析:非限定名称是方法本地或全局。不会自动搜索像类这样的范围。

关于Python如何处理名称的章节和章节在语言参考手册的4.1. Naming and binding部分中。与此相关的位可能是:

  

类块中定义的名称范围仅限于类块;它没有扩展到方法的代码块

将此与函数中定义的名称进行对比, 向下继承(到方法本地函数和类):

  

如果定义发生在功能块中,则范围扩展到定义块中包含的任何块,除非包含的块为名称引入了不同的绑定

答案 3 :(得分:1)

一些评论。

  1. 除非您完全确定必须使用双重下划线,否则不要使用双下划线。

  2. 要访问_assert方法,请使用self调用它。像这样:self._assert(Logger,'Logger还不存在!为什么?')在静态方法中,如在您的示例中,您使用类名:Logger._assert()。 Python非常明确。

  3. 只能在类定义的END处创建类。这就是Python的方式。但是你的错误与此无关。

  4. 我不确定这段代码应该做什么:

    # Settings is a separate class (not dependent on this one).    
    Logger = __createLogger(Settings.LOGGING)
    __clearOldLogs(Settings.LOGGING)
    

    但我怀疑你应该把它放在__init__方法中。在课程构建期间,我没有立即看到任何需要访问该类的代码。

  5. __createLogger是一个奇怪的功能。这个类叫做Logger吗?这不应该只是Logger类的__init__吗?